Blame roles/ipareplica/module_utils/ansible_ipa_replica.py

Packit 8cb997
# -*- coding: utf-8 -*-
Packit 8cb997
Packit 8cb997
# Authors:
Packit 8cb997
#   Thomas Woerner <twoerner@redhat.com>
Packit 8cb997
#
Packit 8cb997
# Based on ipa-replica-install code
Packit 8cb997
#
Packit 8cb997
# Copyright (C) 2018  Red Hat
Packit 8cb997
# see file 'COPYING' for use and warranty information
Packit 8cb997
#
Packit 8cb997
# This program is free software; you can redistribute it and/or modify
Packit 8cb997
# it under the terms of the GNU General Public License as published by
Packit 8cb997
# the Free Software Foundation, either version 3 of the License, or
Packit 8cb997
# (at your option) any later version.
Packit 8cb997
#
Packit 8cb997
# This program is distributed in the hope that it will be useful,
Packit 8cb997
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8cb997
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8cb997
# GNU General Public License for more details.
Packit 8cb997
#
Packit 8cb997
# You should have received a copy of the GNU General Public License
Packit 8cb997
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8cb997
Packit 8cb997
__all__ = ["contextlib", "dnsexception", "dnsresolver", "dnsreversename",
Packit 8cb997
           "parse_version", "IPAChangeConf",
Packit 8cb997
           "certstore", "sysrestore", "ipa_generate_password", "kinit_keytab",
Packit 8cb997
           "IPA_CA_TRUST_FLAGS", "EXTERNAL_CA_TRUST_FLAGS", "DN",
Packit 8cb997
           "ScriptError", "services", "tasks", "constants", "errors", "rpc",
Packit 8cb997
           "x509", "validate_domain_name",
Packit 8cb997
           "no_matching_interface_for_ip_address_warning",
Packit 8cb997
           "configure_krb5_conf", "purge_host_keytab", "adtrust",
Packit 8cb997
           "bindinstance", "ca", "certs", "dns", "httpinstance", "kra",
Packit 8cb997
           "otpdinstance", "custodiainstance", "service", "upgradeinstance",
Packit 8cb997
           "find_providing_servers", "find_providing_server", "load_pkcs12",
Packit 8cb997
           "is_ipa_configured", "ReplicationManager", "replica_conn_check",
Packit 8cb997
           "install_replica_ds", "install_krb", "install_ca_cert",
Packit 8cb997
           "install_http", "install_dns_records", "create_ipa_conf",
Packit 8cb997
           "check_dirsrv", "check_dns_resolution", "configure_certmonger",
Packit 8cb997
           "remove_replica_info_dir", "preserve_enrollment_state",
Packit 8cb997
           "uninstall_client", "promote_sssd", "promote_openldap_conf",
Packit 8cb997
           "rpc_client", "check_remote_fips_mode", "check_remote_version",
Packit 8cb997
           "common_check", "current_domain_level",
Packit 8cb997
           "check_domain_level_is_supported", "promotion_check_ipa_domain",
Packit 8cb997
           "SSSDConfig", "CalledProcessError", "timeconf", "ntpinstance",
Packit 8cb997
           "dnsname", "kernel_keyring", "krbinstance"]
Packit 8cb997
Packit 8cb997
import sys
Packit 8cb997
import logging
Packit 8cb997
from contextlib import contextmanager as contextlib_contextmanager
Packit 8cb997
Packit 8cb997
Packit 8cb997
from ipapython.version import NUM_VERSION, VERSION
Packit 8cb997
Packit 8cb997
if NUM_VERSION < 30201:
Packit 8cb997
    # See ipapython/version.py
Packit 8cb997
    IPA_MAJOR, IPA_MINOR, IPA_RELEASE = [int(x) for x in VERSION.split(".", 2)]
Packit 8cb997
    IPA_PYTHON_VERSION = IPA_MAJOR*10000 + IPA_MINOR*100 + IPA_RELEASE
Packit 8cb997
else:
Packit 8cb997
    IPA_PYTHON_VERSION = NUM_VERSION
Packit 8cb997
Packit 8cb997
Packit 8cb997
if NUM_VERSION >= 40600:
Packit 8cb997
    # IPA version >= 4.6
Packit 8cb997
Packit 8cb997
    import contextlib
Packit 8cb997
Packit 8cb997
    import dns.exception as dnsexception
Packit 8cb997
    import dns.name as dnsname
Packit 8cb997
    import dns.resolver as dnsresolver
Packit 8cb997
    import dns.reversename as dnsreversename
Packit 8cb997
Packit 8cb997
    from pkg_resources import parse_version
Packit 8cb997
Packit 8cb997
    from ipaclient.install.ipachangeconf import IPAChangeConf
Packit 8cb997
    from ipalib.install import certstore, sysrestore
Packit 8cb997
    from ipapython.ipautil import ipa_generate_password
Packit 8cb997
    from ipalib.install.kinit import kinit_keytab
Packit 8cb997
    from ipapython import ipaldap, ipautil, kernel_keyring
Packit 8cb997
    from ipapython.certdb import IPA_CA_TRUST_FLAGS, EXTERNAL_CA_TRUST_FLAGS
Packit 8cb997
    from ipapython.dn import DN
Packit 8cb997
    from ipapython.admintool import ScriptError
Packit 8cb997
    from ipapython.ipa_log_manager import standard_logging_setup
Packit 8cb997
    from ipaplatform import services
Packit 8cb997
    from ipaplatform.tasks import tasks
Packit 8cb997
    from ipaplatform.paths import paths
Packit 8cb997
    from ipalib import api, constants, create_api, errors, rpc, x509
Packit 8cb997
    from ipalib.config import Env
Packit 8cb997
    from ipalib.util import (
Packit 8cb997
        validate_domain_name,
Packit 8cb997
        no_matching_interface_for_ip_address_warning)
Packit 8cb997
    from ipaclient.install.client import configure_krb5_conf, purge_host_keytab
Packit 8cb997
    from ipaserver.install import (
Packit 8cb997
        adtrust, bindinstance, ca, certs, dns, dsinstance, httpinstance,
Packit 8cb997
        installutils, kra, krbinstance,
Packit 8cb997
        otpdinstance, custodiainstance, service, upgradeinstance)
Packit 8cb997
    try:
Packit 8cb997
        from ipaserver.masters import (
Packit 8cb997
            find_providing_servers, find_providing_server)
Packit 8cb997
    except ImportError:
Packit 8cb997
        from ipaserver.install.service import (
Packit 8cb997
            find_providing_servers, find_providing_server)
Packit 8cb997
    from ipaserver.install.installutils import (
Packit 8cb997
        ReplicaConfig, load_pkcs12, is_ipa_configured)
Packit 8cb997
    from ipaserver.install.replication import (
Packit 8cb997
        ReplicationManager, replica_conn_check)
Packit 8cb997
    from ipaserver.install.server.replicainstall import (
Packit 8cb997
        make_pkcs12_info, install_replica_ds, install_krb, install_ca_cert,
Packit 8cb997
        install_http, install_dns_records, create_ipa_conf, check_dirsrv,
Packit 8cb997
        check_dns_resolution, configure_certmonger, remove_replica_info_dir,
Packit 8cb997
        # common_cleanup,
Packit 8cb997
        preserve_enrollment_state, uninstall_client,
Packit 8cb997
        promote_sssd, promote_openldap_conf, rpc_client,
Packit 8cb997
        check_remote_fips_mode, check_remote_version, common_check,
Packit 8cb997
        current_domain_level, check_domain_level_is_supported,
Packit 8cb997
        # enroll_dl0_replica,
Packit 8cb997
        # ensure_enrolled,
Packit 8cb997
        promotion_check_ipa_domain
Packit 8cb997
    )
Packit 8cb997
    import SSSDConfig
Packit 8cb997
    from subprocess import CalledProcessError
Packit 8cb997
Packit 8cb997
    try:
Packit 8cb997
        from ipaclient.install import timeconf
Packit 8cb997
        time_service = "chronyd"
Packit 8cb997
        ntpinstance = None
Packit 8cb997
    except ImportError:
Packit 8cb997
        try:
Packit 8cb997
            from ipaclient.install import ntpconf as timeconf
Packit 8cb997
        except ImportError:
Packit 8cb997
            from ipaclient import ntpconf as timeconf
Packit 8cb997
        from ipaserver.install import ntpinstance
Packit 8cb997
        time_service = "ntpd"
Packit 8cb997
Packit 8cb997
Packit 8cb997
else:
Packit 8cb997
    # IPA version < 4.6
Packit 8cb997
Packit 8cb997
    raise Exception("freeipa version '%s' is too old" % VERSION)
Packit 8cb997
Packit 8cb997
Packit 8cb997
logger = logging.getLogger("ipa-server-install")
Packit Service 0f71a7
Packit Service 0f71a7
Packit Service 0f71a7
def setup_logging():
Packit Service 0f71a7
    # logger.setLevel(logging.DEBUG)
Packit Service 0f71a7
    standard_logging_setup(
Packit Service 0f71a7
        paths.IPAREPLICA_INSTALL_LOG, verbose=False, debug=False,
Packit Service 0f71a7
        filemode='a', console_format='%(message)s')
Packit 8cb997
Packit 8cb997
Packit 8cb997
@contextlib_contextmanager
Packit 8cb997
def redirect_stdout(f):
Packit 8cb997
    sys.stdout = f
Packit 8cb997
    try:
Packit 8cb997
        yield f
Packit 8cb997
    finally:
Packit 8cb997
        sys.stdout = sys.__stdout__
Packit 8cb997
Packit 8cb997
Packit 8cb997
class AnsibleModuleLog():
Packit 8cb997
    def __init__(self, module):
Packit 8cb997
        self.module = module
Packit 8cb997
        _ansible_module_log = self
Packit 8cb997
Packit 8cb997
        class AnsibleLoggingHandler(logging.Handler):
Packit 8cb997
            def emit(self, record):
Packit 8cb997
                _ansible_module_log.write(self.format(record))
Packit 8cb997
Packit 8cb997
        self.logging_handler = AnsibleLoggingHandler()
Packit 8cb997
        logger.setLevel(logging.DEBUG)
Packit 8cb997
        logger.root.addHandler(self.logging_handler)
Packit 8cb997
Packit 8cb997
    def close(self):
Packit 8cb997
        self.flush()
Packit 8cb997
Packit 8cb997
    def flush(self):
Packit 8cb997
        pass
Packit 8cb997
Packit 8cb997
    def log(self, msg):
Packit 8cb997
        # self.write(msg+"\n")
Packit 8cb997
        self.write(msg)
Packit 8cb997
Packit 8cb997
    def debug(self, msg):
Packit 8cb997
        self.module.debug(msg)
Packit 8cb997
Packit 8cb997
    def info(self, msg):
Packit 8cb997
        self.module.debug(msg)
Packit 8cb997
Packit 8cb997
    def write(self, msg):
Packit 8cb997
        self.module.debug(msg)
Packit 8cb997
        # self.module.warn(msg)
Packit 8cb997
Packit 8cb997
Packit 8cb997
class installer_obj(object):
Packit 8cb997
    def __init__(self):
Packit 8cb997
        # CompatServerReplicaInstall
Packit 8cb997
        self.ca_cert_files = None
Packit 8cb997
        self.all_ip_addresses = False
Packit 8cb997
        self.no_wait_for_dns = True
Packit 8cb997
        self.nisdomain = None
Packit 8cb997
        self.no_nisdomain = False
Packit 8cb997
        self.no_sudo = False
Packit 8cb997
        self.request_cert = False
Packit 8cb997
        self.ca_file = None
Packit 8cb997
        self.zonemgr = None
Packit 8cb997
        self.replica_file = None
Packit 8cb997
        # ServerReplicaInstall
Packit 8cb997
        self.subject_base = None
Packit 8cb997
        self.ca_subject = None
Packit 8cb997
        # others
Packit 8cb997
        self._ccache = None
Packit 8cb997
        self.password = None
Packit 8cb997
        self.reverse_zones = []
Packit 8cb997
        # def _is_promote(self):
Packit 8cb997
        #     return self.replica_file is None
Packit 8cb997
        # self.skip_conncheck = False
Packit 8cb997
        self._replica_install = False
Packit 8cb997
        # self.dnssec_master = False # future unknown
Packit 8cb997
        # self.disable_dnssec_master = False # future unknown
Packit 8cb997
        # self.domainlevel = MAX_DOMAIN_LEVEL # deprecated
Packit 8cb997
        # self.domain_level = self.domainlevel # deprecated
Packit 8cb997
        self.interactive = False
Packit 8cb997
        self.unattended = not self.interactive
Packit 8cb997
        # self.promote = self.replica_file is None
Packit 8cb997
        self.promote = True
Packit 8cb997
        self.skip_schema_check = None
Packit 8cb997
Packit 8cb997
    # def __getattribute__(self, attr):
Packit 8cb997
    #     value = super(installer_obj, self).__getattribute__(attr)
Packit 8cb997
    #     if not attr.startswith("--") and not attr.endswith("--"):
Packit 8cb997
    #         logger.debug(
Packit 8cb997
    #             "  <-- Accessing installer.%s (%s)" % (attr, repr(value)))
Packit 8cb997
    #     return value
Packit 8cb997
Packit 8cb997
    def __getattr__(self, attr):
Packit 8cb997
        logger.info("  --> ADDING missing installer.%s", attr)
Packit 8cb997
        setattr(self, attr, None)
Packit 8cb997
        return getattr(self, attr)
Packit 8cb997
Packit 8cb997
    # def __setattr__(self, attr, value):
Packit 8cb997
    #    logger.debug("  --> Setting installer.%s to %s" % (attr, repr(value)))
Packit 8cb997
    #    return super(installer_obj, self).__setattr__(attr, value)
Packit 8cb997
Packit 8cb997
    def knobs(self):
Packit 8cb997
        for name in self.__dict__:
Packit 8cb997
            yield self, name
Packit 8cb997
Packit 8cb997
Packit 8cb997
installer = installer_obj()
Packit 8cb997
options = installer
Packit 8cb997
Packit 8cb997
# DNSInstallInterface
Packit 8cb997
options.dnssec_master = False
Packit 8cb997
options.disable_dnssec_master = False
Packit 8cb997
options.kasp_db_file = None
Packit 8cb997
options.force = False
Packit 8cb997
Packit 8cb997
# ServerMasterInstall
Packit 8cb997
options.add_sids = False
Packit 8cb997
options.add_agents = False
Packit 8cb997
Packit 8cb997
# ServerReplicaInstall
Packit 8cb997
options.subject_base = None
Packit 8cb997
options.ca_subject = None
Packit 8cb997
Packit 8cb997
Packit 8cb997
def gen_env_boostrap_finalize_core(etc_ipa, default_config):
Packit 8cb997
    env = Env()
Packit 8cb997
    # env._bootstrap(context='installer', confdir=paths.ETC_IPA, log=None)
Packit 8cb997
    # env._finalize_core(**dict(constants.DEFAULT_CONFIG))
Packit 8cb997
    env._bootstrap(context='installer', confdir=etc_ipa, log=None)
Packit 8cb997
    env._finalize_core(**dict(default_config))
Packit 8cb997
    return env
Packit 8cb997
Packit 8cb997
Packit 8cb997
def api_bootstrap_finalize(env):
Packit 8cb997
    # pylint: disable=no-member
Packit 8cb997
    xmlrpc_uri = 'https://{}/ipa/xml'.format(ipautil.format_netloc(env.host))
Packit 8cb997
    api.bootstrap(in_server=True,
Packit 8cb997
                  context='installer',
Packit 8cb997
                  confdir=paths.ETC_IPA,
Packit 8cb997
                  ldap_uri=installutils.realm_to_ldapi_uri(env.realm),
Packit 8cb997
                  xmlrpc_uri=xmlrpc_uri)
Packit 8cb997
    # pylint: enable=no-member
Packit 8cb997
    api.finalize()
Packit 8cb997
Packit 8cb997
Packit 8cb997
def gen_ReplicaConfig():
Packit 8cb997
    class ExtendedReplicaConfig(ReplicaConfig):
Packit 8cb997
        def __init__(self, top_dir=None):
Packit 8cb997
            super(ExtendedReplicaConfig, self).__init__(top_dir)
Packit 8cb997
Packit 8cb997
        # def __getattribute__(self, attr):
Packit 8cb997
        #    value = super(ExtendedReplicaConfig, self).__getattribute__(attr)
Packit 8cb997
        #    if attr not in ["__dict__", "knobs"]:
Packit 8cb997
        #        logger.debug("  <== Accessing config.%s (%s)" %
Packit 8cb997
        #                     (attr, repr(value)))
Packit 8cb997
        #    return value
Packit 8cb997
Packit 8cb997
        def __getattr__(self, attr):
Packit 8cb997
            logger.info("  ==> ADDING missing config.%s", attr)
Packit 8cb997
            setattr(self, attr, None)
Packit 8cb997
            return getattr(self, attr)
Packit 8cb997
Packit 8cb997
        # def __setattr__(self, attr, value):
Packit 8cb997
        #   logger.debug("  ==> Setting config.%s to %s" % (attr, repr(value)))
Packit 8cb997
        #   return super(ExtendedReplicaConfig, self).__setattr__(attr, value)
Packit 8cb997
Packit 8cb997
        def knobs(self):
Packit 8cb997
            for name in self.__dict__:
Packit 8cb997
                yield self, name
Packit 8cb997
Packit 8cb997
    # config = ReplicaConfig()
Packit 8cb997
    config = ExtendedReplicaConfig()
Packit 8cb997
    config.realm_name = api.env.realm
Packit 8cb997
    config.host_name = api.env.host
Packit 8cb997
    config.domain_name = api.env.domain
Packit 8cb997
    config.master_host_name = api.env.server
Packit 8cb997
    config.ca_host_name = api.env.ca_host
Packit 8cb997
    config.kra_host_name = config.ca_host_name
Packit 8cb997
    config.ca_ds_port = 389
Packit 8cb997
    config.setup_ca = options.setup_ca
Packit 8cb997
    config.setup_kra = options.setup_kra
Packit 8cb997
    config.dir = options._top_dir
Packit 8cb997
    config.basedn = api.env.basedn
Packit 8cb997
    # config.subject_base = options.subject_base
Packit 8cb997
Packit 8cb997
    return config
Packit 8cb997
Packit 8cb997
Packit 8cb997
def replica_ds_init_info(ansible_log,
Packit 8cb997
                         config, options, ca_is_configured, remote_api,
Packit 8cb997
                         ds_ca_subject, ca_file,
Packit 8cb997
                         promote=False, pkcs12_info=None):
Packit 8cb997
Packit 8cb997
    dsinstance.check_ports()
Packit 8cb997
Packit 8cb997
    # if we have a pkcs12 file, create the cert db from
Packit 8cb997
    # that. Otherwise the ds setup will create the CA
Packit 8cb997
    # cert
Packit 8cb997
    if pkcs12_info is None:
Packit 8cb997
        pkcs12_info = make_pkcs12_info(config.dir, "dscert.p12",
Packit 8cb997
                                       "dirsrv_pin.txt")
Packit 8cb997
Packit 8cb997
    # during replica install, this gets invoked before local DS is
Packit 8cb997
    # available, so use the remote api.
Packit 8cb997
    # if ca_is_configured:
Packit 8cb997
    #     ca_subject = ca.lookup_ca_subject(_api, config.subject_base)
Packit 8cb997
    # else:
Packit 8cb997
    #     ca_subject = installutils.default_ca_subject_dn(config.subject_base)
Packit 8cb997
    ca_subject = ds_ca_subject
Packit 8cb997
Packit 8cb997
    ds = dsinstance.DsInstance(
Packit 8cb997
        config_ldif=options.dirsrv_config_file)
Packit 8cb997
    ds.set_output(ansible_log)
Packit 8cb997
Packit 8cb997
    # Source: ipaserver/install/dsinstance.py
Packit 8cb997
Packit 8cb997
    # idstart and idmax are configured so that the range is seen as
Packit 8cb997
    # depleted by the DNA plugin and the replica will go and get a
Packit 8cb997
    # new range from the master.
Packit 8cb997
    # This way all servers use the initially defined range by default.
Packit 8cb997
    idstart = 1101
Packit 8cb997
    idmax = 1100
Packit 8cb997
Packit 8cb997
    with redirect_stdout(ansible_log):
Packit 8cb997
        ds.init_info(
Packit 8cb997
            realm_name=config.realm_name,
Packit 8cb997
            fqdn=config.host_name,
Packit 8cb997
            domain_name=config.domain_name,
Packit 8cb997
            dm_password=config.dirman_password,
Packit 8cb997
            subject_base=config.subject_base,
Packit 8cb997
            ca_subject=ca_subject,
Packit 8cb997
            idstart=idstart,
Packit 8cb997
            idmax=idmax,
Packit 8cb997
            pkcs12_info=pkcs12_info,
Packit 8cb997
            ca_file=ca_file,
Packit 8cb997
            setup_pkinit=not options.no_pkinit,
Packit 8cb997
        )
Packit 8cb997
    ds.master_fqdn = config.master_host_name
Packit 8cb997
    if ca_is_configured is not None:
Packit 8cb997
        ds.ca_is_configured = ca_is_configured
Packit 8cb997
    ds.promote = promote
Packit 8cb997
    ds.api = remote_api
Packit 8cb997
Packit 8cb997
    # from __setup_replica
Packit 8cb997
Packit 8cb997
    # Always connect to ds over ldapi
Packit 8cb997
    ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=ds.realm)
Packit 8cb997
    conn = ipaldap.LDAPClient(ldap_uri)
Packit 8cb997
    conn.external_bind()
Packit 8cb997
Packit 8cb997
    return ds
Packit 8cb997
Packit 8cb997
Packit 8cb997
def ansible_module_get_parsed_ip_addresses(ansible_module,
Packit 8cb997
                                           param='ip_addresses'):
Packit 8cb997
    ip_addrs = []
Packit 8cb997
    for ip in ansible_module.params.get(param):
Packit 8cb997
        try:
Packit 8cb997
            ip_parsed = ipautil.CheckedIPAddress(ip)
Packit 8cb997
        except Exception as e:
Packit 8cb997
            ansible_module.fail_json(msg="Invalid IP Address %s: %s" % (ip, e))
Packit 8cb997
        ip_addrs.append(ip_parsed)
Packit 8cb997
    return ip_addrs
Packit 8cb997
Packit 8cb997
Packit 8cb997
def gen_remote_api(master_host_name, etc_ipa):
Packit 8cb997
    ldapuri = 'ldaps://%s' % ipautil.format_netloc(master_host_name)
Packit 8cb997
    xmlrpc_uri = 'https://{}/ipa/xml'.format(
Packit 8cb997
        ipautil.format_netloc(master_host_name))
Packit 8cb997
    remote_api = create_api(mode=None)
Packit 8cb997
    remote_api.bootstrap(in_server=True,
Packit 8cb997
                         context='installer',
Packit 8cb997
                         confdir=etc_ipa,
Packit 8cb997
                         ldap_uri=ldapuri,
Packit 8cb997
                         xmlrpc_uri=xmlrpc_uri)
Packit 8cb997
    remote_api.finalize()
Packit 8cb997
    return remote_api