# -*- coding: utf-8 -*-

# Authors:
#   Thomas Woerner <twoerner@redhat.com>
#
# Based on ipa-replica-install code
#
# Copyright (C) 2018-2022  Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import (absolute_import, division, print_function)

__metaclass__ = type

ANSIBLE_METADATA = {
    'metadata_version': '1.0',
    'supported_by': 'community',
    'status': ['preview'],
}

DOCUMENTATION = '''
---
module: ipareplica_prepare
short_description: Prepare ipa replica installation
description: |
  Prepare ipa replica installation: Create IPA configuration file, run install
  checks again and also update the host name and the hosts file if needed.
  The tests and also the results from ipareplica_test are needed.
options:
  dm_password:
    description: Directory Manager password
    type: str
    required: no
  password:
    description: Admin user kerberos password
    type: str
    required: no
  ip_addresses:
    description: List of Master Server IP Addresses
    type: list
    elements: str
    required: no
  domain:
    description: Primary DNS domain of the IPA deployment
    type: str
    required: no
  realm:
    description: Kerberos realm name of the IPA deployment
    type: str
    required: no
  hostname:
    description: Fully qualified name of this host
    type: str
    required: no
  principal:
    description:
      User Principal allowed to promote replicas and join IPA realm
    type: str
    required: true
  ca_cert_files:
    description:
      List of files containing CA certificates for the service certificate
      files
    type: list
    elements: str
    required: no
  no_host_dns:
    description: Do not use DNS for hostname lookup during installation
    type: bool
    default: no
    required: no
  setup_adtrust:
    description: Configure AD trust capability
    type: bool
    required: no
  setup_ca:
    description: Configure a dogtag CA
    type: bool
    required: no
  setup_kra:
    description: Configure a dogtag KRA
    type: bool
    required: no
  setup_dns:
    description: Configure bind with our zone
    type: bool
    required: no
  dirsrv_cert_files:
    description:
      Files containing the Directory Server SSL certificate and private key
    type: list
    elements: str
    required: no
  dirsrv_cert_name:
    description: Name of the Directory Server SSL certificate to install
    type: str
    required: no
  dirsrv_pin:
    description: The password to unlock the Directory Server private key
    type: str
    required: no
  http_cert_files:
    description:
      File containing the Apache Server SSL certificate and private key
    type: list
    elements: str
    required: no
  http_cert_name:
    description: Name of the Apache Server SSL certificate to install
    type: str
    required: no
  http_pin:
    description: The password to unlock the Apache Server private key
    type: str
    required: no
  pkinit_cert_files:
    description:
      File containing the Kerberos KDC SSL certificate and private key
    type: list
    elements: str
    required: no
  pkinit_cert_name:
    description: Name of the Kerberos KDC SSL certificate to install
    type: str
    required: no
  pkinit_pin:
    description: The password to unlock the Kerberos KDC private key
    type: str
    required: no
  keytab:
    description: Path to backed up keytab from previous enrollment
    type: str
    required: no
  mkhomedir:
    description: Create home directories for users on their first login
    type: bool
    required: no
  force_join:
    description: Force client enrollment even if already enrolled
    type: bool
    required: no
  no_ntp:
    description: Do not configure ntp
    type: bool
    required: no
  ssh_trust_dns:
    description: Configure OpenSSH client to trust DNS SSHFP records
    type: bool
    required: no
  no_ssh:
    description: Do not configure OpenSSH client
    type: bool
    required: no
  no_sshd:
    description: Do not configure OpenSSH server
    type: bool
    required: no
  no_dns_sshfp:
    description: Do not automatically create DNS SSHFP records
    type: bool
    required: no
  allow_zone_overlap:
    description: Create DNS zone even if it already exists
    type: bool
    default: no
    required: no
  reverse_zones:
    description: The reverse DNS zones to use
    type: list
    elements: str
    required: no
  no_reverse:
    description: Do not create new reverse DNS zone
    type: bool
    default: no
    required: no
  auto_reverse:
    description: Create necessary reverse zones
    type: bool
    default: no
    required: no
  forwarders:
    description: Add DNS forwarders
    type: list
    elements: str
    required: no
  no_forwarders:
    description: Do not add any DNS forwarders, use root servers instead
    type: bool
    default: no
    required: no
  auto_forwarders:
    description: Use DNS forwarders configured in /etc/resolv.conf
    type: bool
    default: no
    required: no
  forward_policy:
    description: DNS forwarding policy for global forwarders
    type: str
    choices: ['first', 'only']
    required: no
  no_dnssec_validation:
    description: Disable DNSSEC validation
    type: bool
    default: no
    required: no
  enable_compat:
    description: Enable support for trusted domains for old clients
    type: bool
    default: no
    required: no
  netbios_name:
    description: NetBIOS name of the IPA domain
    type: str
    required: no
  rid_base:
    description: Start value for mapping UIDs and GIDs to RIDs
    type: int
    default: 1000
    required: no
  secondary_rid_base:
    description:
      Start value of the secondary range for mapping UIDs and GIDs to RIDs
    type: int
    default: 100000000
    required: no
  server:
    description: Fully qualified name of IPA server to enroll to
    type: str
    required: true
  skip_conncheck:
    description: Skip connection check to remote master
    type: bool
    required: no
  sid_generation_always:
    description: Enable SID generation always
    type: bool
    default: no
    required: no
author:
    - Thomas Woerner (@t-woerner)
'''

EXAMPLES = '''
'''

RETURN = '''
'''

import os
import tempfile
import traceback
from shutil import copyfile

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ansible_ipa_replica import (
    check_imports, AnsibleModuleLog, options, installer, DN, paths, sysrestore,
    ansible_module_get_parsed_ip_addresses, Env, ipautil, ipaldap,
    installutils, ReplicaConfig, load_pkcs12, kinit_keytab, create_api,
    rpc_client, check_remote_version, parse_version, check_remote_fips_mode,
    ReplicationManager, promotion_check_ipa_domain, current_domain_level,
    check_domain_level_is_supported, errors, ScriptError, setup_logging,
    logger, check_dns_resolution, service, find_providing_server, ca, kra,
    dns, no_matching_interface_for_ip_address_warning, adtrust,
    constants, api, redirect_stdout, replica_conn_check, tasks
)
from ansible.module_utils import six

if six.PY3:
    unicode = str


def main():
    ansible_module = AnsibleModule(
        argument_spec=dict(
            # basic
            dm_password=dict(required=False, type='str', no_log=True),
            password=dict(required=False, type='str', no_log=True),
            ip_addresses=dict(required=False, type='list', elements='str',
                              default=[]),
            domain=dict(required=False, type='str'),
            realm=dict(required=False, type='str'),
            hostname=dict(required=False, type='str'),
            principal=dict(required=True, type='str'),
            ca_cert_files=dict(required=False, type='list', elements='str',
                               default=[]),
            no_host_dns=dict(required=False, type='bool', default=False),
            # server
            setup_adtrust=dict(required=False, type='bool'),
            setup_ca=dict(required=False, type='bool'),
            setup_kra=dict(required=False, type='bool'),
            setup_dns=dict(required=False, type='bool'),
            # ssl certificate
            dirsrv_cert_files=dict(required=False, type='list', elements='str',
                                   default=[]),
            dirsrv_cert_name=dict(required=False, type='str'),
            dirsrv_pin=dict(required=False, type='str'),
            http_cert_files=dict(required=False, type='list', elements='str',
                                 default=[]),
            http_cert_name=dict(required=False, type='str'),
            http_pin=dict(required=False, type='str'),
            pkinit_cert_files=dict(required=False, type='list', elements='str',
                                   default=[]),
            pkinit_cert_name=dict(required=False, type='str'),
            pkinit_pin=dict(required=False, type='str'),
            # client
            keytab=dict(required=False, type='str', no_log=False),
            mkhomedir=dict(required=False, type='bool'),
            force_join=dict(required=False, type='bool'),
            no_ntp=dict(required=False, type='bool'),
            ssh_trust_dns=dict(required=False, type='bool'),
            no_ssh=dict(required=False, type='bool'),
            no_sshd=dict(required=False, type='bool'),
            no_dns_sshfp=dict(required=False, type='bool'),
            # certificate system
            # subject_base=dict(required=False),
            # dns
            allow_zone_overlap=dict(required=False, type='bool',
                                    default=False),
            reverse_zones=dict(required=False, type='list', elements='str',
                               default=[]),
            no_reverse=dict(required=False, type='bool', default=False),
            auto_reverse=dict(required=False, type='bool', default=False),
            forwarders=dict(required=False, type='list', elements='str',
                            default=[]),
            no_forwarders=dict(required=False, type='bool', default=False),
            auto_forwarders=dict(required=False, type='bool', default=False),
            forward_policy=dict(required=False, type='str',
                                choices=['first', 'only'], default=None),
            no_dnssec_validation=dict(required=False, type='bool',
                                      default=False),
            # ad trust
            enable_compat=dict(required=False, type='bool', default=False),
            netbios_name=dict(required=False, type='str'),
            rid_base=dict(required=False, type='int', default=1000),
            secondary_rid_base=dict(required=False, type='int',
                                    default=100000000),
            # additional
            server=dict(required=True, type='str'),
            skip_conncheck=dict(required=False, type='bool'),
            sid_generation_always=dict(required=False, type='bool',
                                       default=False),
        ),
        supports_check_mode=False,
    )

    ansible_module._ansible_debug = True
    check_imports(ansible_module)
    setup_logging()
    ansible_log = AnsibleModuleLog(ansible_module)

    # get parameters #

    options.dm_password = ansible_module.params.get('dm_password')
    options.password = options.dm_password
    options.admin_password = ansible_module.params.get('password')
    options.ip_addresses = ansible_module_get_parsed_ip_addresses(
        ansible_module)
    options.domain_name = ansible_module.params.get('domain')
    options.realm_name = ansible_module.params.get('realm')
    options.host_name = ansible_module.params.get('hostname')
    options.principal = ansible_module.params.get('principal')
    options.ca_cert_files = ansible_module.params.get('ca_cert_files')
    options.no_host_dns = ansible_module.params.get('no_host_dns')
    # server
    options.setup_adtrust = ansible_module.params.get('setup_adtrust')
    options.setup_ca = ansible_module.params.get('setup_ca')
    options.setup_kra = ansible_module.params.get('setup_kra')
    options.setup_dns = ansible_module.params.get('setup_dns')
    # ssl certificate
    options.dirsrv_cert_files = ansible_module.params.get('dirsrv_cert_files')
    options.dirsrv_cert_name = ansible_module.params.get('dirsrv_cert_name')
    options.dirsrv_pin = ansible_module.params.get('dirsrv_pin')
    options.http_cert_files = ansible_module.params.get('http_cert_files')
    options.http_cert_name = ansible_module.params.get('http_cert_name')
    options.http_pin = ansible_module.params.get('http_pin')
    options.pkinit_cert_files = ansible_module.params.get('pkinit_cert_files')
    options.pkinit_cert_name = ansible_module.params.get('pkinit_cert_name')
    options.pkinit_pin = ansible_module.params.get('pkinit_pin')
    # client
    options.keytab = ansible_module.params.get('keytab')
    options.mkhomedir = ansible_module.params.get('mkhomedir')
    options.force_join = ansible_module.params.get('force_join')
    options.no_ntp = ansible_module.params.get('no_ntp')
    options.ssh_trust_dns = ansible_module.params.get('ssh_trust_dns')
    options.no_ssh = ansible_module.params.get('no_ssh')
    options.no_sshd = ansible_module.params.get('no_sshd')
    options.no_dns_sshfp = ansible_module.params.get('no_dns_sshfp')
    # certificate system
    options.external_ca = ansible_module.params.get('external_ca')
    options.external_cert_files = ansible_module.params.get(
        'external_cert_files')
    # options.subject_base = ansible_module.params.get('subject_base')
    # options.ca_subject = ansible_module.params.get('ca_subject')
    # dns
    options.allow_zone_overlap = ansible_module.params.get(
        'allow_zone_overlap')
    options.reverse_zones = ansible_module.params.get('reverse_zones')
    options.no_reverse = ansible_module.params.get('no_reverse')
    options.auto_reverse = ansible_module.params.get('auto_reverse')
    options.forwarders = ansible_module.params.get('forwarders')
    options.no_forwarders = ansible_module.params.get('no_forwarders')
    options.auto_forwarders = ansible_module.params.get('auto_forwarders')
    options.forward_policy = ansible_module.params.get('forward_policy')
    options.no_dnssec_validation = ansible_module.params.get(
        'no_dnssec_validation')
    # ad trust
    options.enable_compat = ansible_module.params.get('enable_compat')
    options.netbios_name = ansible_module.params.get('netbios_name')
    options.rid_base = ansible_module.params.get('rid_base')
    options.secondary_rid_base = ansible_module.params.get(
        'secondary_rid_base')

    # additional
    # options._host_name_overridden = ansible_module.params.get(
    #     '_hostname_overridden')
    options.server = ansible_module.params.get('server')
    options.skip_conncheck = ansible_module.params.get('skip_conncheck')
    sid_generation_always = ansible_module.params.get('sid_generation_always')

    # random serial numbers are master_only, therefore setting to False
    options.random_serial_numbers = False
    # options._random_serial_numbers is generated by ca.install_check and
    # later used by ca.install in the _setup_ca module.
    options._random_serial_numbers = False

    # init #

    fstore = sysrestore.FileStore(paths.SYSRESTORE)
    sstore = sysrestore.StateFile(paths.SYSRESTORE)

    # prepare (install prepare, install checks) #

    ##########################################################################
    # replica promote_check ##################################################
    ##########################################################################

    ansible_log.debug("== PROMOTE CHECK ==")

    # ansible_log.debug("-- NO_NTP --") # already done in test

    # check selinux status, http and DS ports, NTP conflicting services
    # common_check(options.no_ntp)

    installer._enrollment_performed = False
    installer._top_dir = tempfile.mkdtemp("ipa")

    # with ipautil.private_ccache():
    dir_path = tempfile.mkdtemp(prefix='krbcc')
    os.environ['KRB5CCNAME'] = os.path.join(dir_path, 'ccache')

    ansible_log.debug("-- API --")

    env = Env()
    env._bootstrap(context='installer', confdir=paths.ETC_IPA, log=None)
    env._finalize_core(**dict(constants.DEFAULT_CONFIG))

    # pylint: disable=no-member
    xmlrpc_uri = 'https://{0}/ipa/xml'.format(ipautil.format_netloc(env.host))
    if hasattr(ipaldap, "realm_to_ldapi_uri"):
        realm_to_ldapi_uri = ipaldap.realm_to_ldapi_uri
    else:
        realm_to_ldapi_uri = installutils.realm_to_ldapi_uri
    api.bootstrap(in_server=True,
                  context='installer',
                  confdir=paths.ETC_IPA,
                  ldap_uri=realm_to_ldapi_uri(env.realm),
                  xmlrpc_uri=xmlrpc_uri)
    # pylint: enable=no-member
    api.finalize()

    ansible_log.debug("-- REPLICA_CONFIG --")

    config = ReplicaConfig()
    config.realm_name = api.env.realm
    config.host_name = api.env.host
    config.domain_name = api.env.domain
    config.master_host_name = api.env.server
    if not api.env.ca_host or api.env.ca_host == api.env.host:
        # ca_host has not been configured explicitly, prefer source master
        config.ca_host_name = api.env.server
    else:
        # default to ca_host from IPA config
        config.ca_host_name = api.env.ca_host
    config.kra_host_name = config.ca_host_name
    config.ca_ds_port = 389
    config.setup_ca = options.setup_ca
    config.setup_kra = options.setup_kra
    config.dir = installer._top_dir
    config.basedn = api.env.basedn
    # config.hidden_replica = options.hidden_replica

    # load and check certificates #

    ansible_log.debug("-- CERT_FILES --")

    http_pkcs12_file = None
    http_pkcs12_info = None
    http_ca_cert = None
    dirsrv_pkcs12_file = None
    dirsrv_pkcs12_info = None
    dirsrv_ca_cert = None
    pkinit_pkcs12_file = None
    pkinit_pkcs12_info = None
    pkinit_ca_cert = None

    if options.http_cert_files:
        ansible_log.debug("-- HTTP_CERT_FILES --")
        if options.http_pin is None:
            ansible_module.fail_json(
                msg="Apache Server private key unlock password required")
        http_pkcs12_file, http_pin, http_ca_cert = load_pkcs12(
            cert_files=options.http_cert_files,
            key_password=options.http_pin,
            key_nickname=options.http_cert_name,
            ca_cert_files=options.ca_cert_files,
            host_name=config.host_name)
        http_pkcs12_info = (http_pkcs12_file.name, http_pin)

    if options.dirsrv_cert_files:
        ansible_log.debug("-- DIRSRV_CERT_FILES --")
        if options.dirsrv_pin is None:
            ansible_module.fail_json(
                msg="Directory Server private key unlock password required")
        dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = load_pkcs12(
            cert_files=options.dirsrv_cert_files,
            key_password=options.dirsrv_pin,
            key_nickname=options.dirsrv_cert_name,
            ca_cert_files=options.ca_cert_files,
            host_name=config.host_name)
        dirsrv_pkcs12_info = (dirsrv_pkcs12_file.name, dirsrv_pin)

    if options.pkinit_cert_files:
        ansible_log.debug("-- PKINIT_CERT_FILES --")
        if options.pkinit_pin is None:
            ansible_module.fail_json(
                msg="Kerberos KDC private key unlock password required")
        pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12(
            cert_files=options.pkinit_cert_files,
            key_password=options.pkinit_pin,
            key_nickname=options.pkinit_cert_name,
            ca_cert_files=options.ca_cert_files,
            realm_name=config.realm_name)
        pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin)

    if (options.http_cert_files and options.dirsrv_cert_files and
            http_ca_cert != dirsrv_ca_cert):
        ansible_module.fail_json(
            msg="Apache Server SSL certificate and Directory "
            "Server SSL certificate are not signed by the same"
            " CA certificate")

    if (options.http_cert_files and
            options.pkinit_cert_files and
            http_ca_cert != pkinit_ca_cert):
        ansible_module.fail_json(
            msg="Apache Server SSL certificate and PKINIT KDC "
            "certificate are not signed by the same CA "
            "certificate")

    # Copy pkcs12_files to make them persistent till deployment is done
    # and encode certificates for ansible compatibility
    if http_pkcs12_info is not None:
        copyfile(http_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_http")
        http_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_http", http_pin)
        http_ca_cert = ""
    if dirsrv_pkcs12_info is not None:
        copyfile(dirsrv_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_dirsrv")
        dirsrv_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_dirsrv", dirsrv_pin)
        dirsrv_ca_cert = ""
    if pkinit_pkcs12_info is not None:
        copyfile(pkinit_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_pkinit")
        pkinit_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_pkinit", pkinit_pin)
        pkinit_ca_cert = ""

    ansible_log.debug("-- FQDN --")

    installutils.verify_fqdn(config.host_name, options.no_host_dns)
    installutils.verify_fqdn(config.master_host_name, options.no_host_dns)

    ansible_log.debug("-- KINIT_KEYTAB --")

    ccache = os.environ['KRB5CCNAME']
    kinit_keytab('host/{env.host}@{env.realm}'.format(env=api.env),
                 paths.KRB5_KEYTAB,
                 ccache)

    ansible_log.debug("-- CA_CRT --")

    cafile = paths.IPA_CA_CRT
    if not os.path.isfile(cafile):
        ansible_module.fail_json(
            msg="CA cert file is not available! Please reinstall"
            "the client and try again.")

    ansible_log.debug("-- REMOTE_API --")

    ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
    xmlrpc_uri = 'https://{0}/ipa/xml'.format(
        ipautil.format_netloc(config.master_host_name))
    remote_api = create_api(mode=None)
    remote_api.bootstrap(in_server=True,
                         context='installer',
                         confdir=paths.ETC_IPA,
                         ldap_uri=ldapuri,
                         xmlrpc_uri=xmlrpc_uri)
    remote_api.finalize()
    installer._remote_api = remote_api

    ansible_log.debug("-- RPC_CLIENT --")

    with rpc_client(remote_api) as client:
        check_remote_version(client, parse_version(api.env.version))
        check_remote_fips_mode(client, api.env.fips_mode)

    conn = remote_api.Backend.ldap2
    replman = None
    try:
        ansible_log.debug("-- CONNECT --")
        # Try out authentication
        conn.connect(ccache=ccache)
        replman = ReplicationManager(config.realm_name,
                                     config.master_host_name, None)

        ansible_log.debug("-- CHECK IPA_DOMAIN --")

        promotion_check_ipa_domain(conn, remote_api.env.basedn)

        ansible_log.debug("-- CHECK DOMAIN_LEVEL --")

        # Make sure that domain fulfills minimal domain level
        # requirement
        domain_level = current_domain_level(remote_api)
        check_domain_level_is_supported(domain_level)
        if domain_level < constants.MIN_DOMAIN_LEVEL:
            ansible_module.fail_json(
                msg="Cannot promote this client to a replica. The domain "
                "level "
                "must be raised to {mindomainlevel} before the replica can be "
                "installed".format(
                    mindomainlevel=constants.MIN_DOMAIN_LEVEL))

        ansible_log.debug("-- CHECK AUTHORIZATION --")

        # Check authorization
        result = remote_api.Command['hostgroup_find'](
            cn=u'ipaservers',
            host=[unicode(api.env.host)]  # pylint: disable=W0012,E0606
        )['result']
        add_to_ipaservers = not result

        ansible_log.debug("-- ADD_TO_IPASERVERS --")

        if add_to_ipaservers:
            if options.password and not options.admin_password:
                raise errors.ACIError(info="Not authorized")

            if installer._ccache is None:
                os.environ.pop('KRB5CCNAME', None)
            else:
                os.environ['KRB5CCNAME'] = installer._ccache

            try:
                installutils.check_creds(options, config.realm_name)
                installer._ccache = os.environ.get('KRB5CCNAME')
            finally:
                os.environ['KRB5CCNAME'] = ccache

            conn.disconnect()
            conn.connect(ccache=installer._ccache)

            try:
                result = remote_api.Command['hostgroup_show'](
                    u'ipaservers',
                    all=True,
                    rights=True
                )['result']

                if 'w' not in result['attributelevelrights']['member']:
                    raise errors.ACIError(info="Not authorized")
            finally:
                ansible_log.debug("-- RECONNECT --")
                conn.disconnect()
                conn.connect(ccache=ccache)

        ansible_log.debug("-- CHECK FOR REPLICATION AGREEMENT --")

        # Check that we don't already have a replication agreement
        if replman.get_replication_agreement(config.host_name):
            msg = ("A replication agreement for this host already exists. "
                   "It needs to be removed.\n"
                   "Run this command:\n"
                   "    %% ipa-replica-manage del {host} --force"
                   .format(host=config.host_name))
            raise ScriptError(msg, rval=3)

        ansible_log.debug("-- DETECT REPLICATION MANAGER GROUP --")

        # Detect if the other master can handle replication managers
        # cn=replication managers,cn=sysaccounts,cn=etc,$SUFFIX
        dn = DN(('cn', 'replication managers'), ('cn', 'sysaccounts'),
                ('cn', 'etc'), ipautil.realm_to_suffix(config.realm_name))
        try:
            conn.get_entry(dn)
        except errors.NotFound:
            msg = ("The Replication Managers group is not available in "
                   "the domain. Replica promotion requires the use of "
                   "Replication Managers to be able to replicate data. "
                   "Upgrade the peer master or use the ipa-replica-prepare "
                   "command on the master and use a prep file to install "
                   "this replica.")
            logger.error("%s", msg)
            raise ScriptError(msg, rval=3)

        ansible_log.debug("-- CHECK DNS_MASTERS --")

        dns_masters = remote_api.Object['dnsrecord'].get_dns_masters()
        if dns_masters:
            if not options.no_host_dns:
                logger.debug('Check forward/reverse DNS resolution')
                resolution_ok = (
                    check_dns_resolution(config.master_host_name,
                                         dns_masters) and
                    check_dns_resolution(config.host_name, dns_masters))
                if not resolution_ok and installer.interactive:
                    if not ipautil.user_input("Continue?", False):
                        raise ScriptError(rval=0)
        else:
            logger.debug('No IPA DNS servers, '
                         'skipping forward/reverse resolution check')

        ansible_log.debug("-- GET_IPA_CONFIG --")

        entry_attrs = conn.get_ipa_config()
        subject_base = entry_attrs.get('ipacertificatesubjectbase', [None])[0]
        if subject_base is not None:
            config.subject_base = DN(subject_base)

        ansible_log.debug("-- SEARCH FOR CA --")

        # Find if any server has a CA
        if not hasattr(service, "find_providing_server"):
            _host = [config.ca_host_name]
        else:
            _host = config.ca_host_name
        ca_host = find_providing_server('CA', conn, _host)
        if ca_host is not None:
            config.ca_host_name = ca_host
            ca_enabled = True
            if options.dirsrv_cert_files:
                msg = ("Certificates could not be provided when "
                       "CA is present on some master.")
                logger.error(msg)
                raise ScriptError(msg, rval=3)
        else:
            if options.setup_ca:
                msg = ("The remote master does not have a CA "
                       "installed, can't set up CA")
                logger.error(msg)
                raise ScriptError(msg, rval=3)
            ca_enabled = False
            if not options.dirsrv_cert_files:
                msg = ("Cannot issue certificates: a CA is not "
                       "installed. Use the --http-cert-file, "
                       "--dirsrv-cert-file options to provide "
                       "custom certificates.")
                logger.error(msg)
                raise ScriptError(msg, rval=3)

        ansible_log.debug("-- SEARCH FOR KRA --")

        if not hasattr(service, "find_providing_server"):
            _host = [config.kra_host_name]
        else:
            _host = config.kra_host_name
        kra_host = find_providing_server('KRA', conn, _host)
        if kra_host is not None:
            config.kra_host_name = kra_host
            kra_enabled = True
        else:
            if options.setup_kra:
                msg = ("There is no active KRA server in the domain, "
                       "can't setup a KRA clone")
                logger.error(msg)
                raise ScriptError(msg, rval=3)
            kra_enabled = False

        ansible_log.debug("-- CHECK CA --")

        if ca_enabled:
            options.realm_name = config.realm_name
            options.host_name = config.host_name
            ca.install_check(False, config, options)

            ansible_log.debug("  ca.external_cert_file=%s" %
                              repr(ca.external_cert_file))
            ansible_log.debug("  ca.external_ca_file=%s" %
                              repr(ca.external_ca_file))

            # TODO
            # TODO
            # Save global vars external_cert_file, external_ca_file for
            # later use
            # TODO
            # TODO

        ansible_log.debug("-- CHECK KRA --")

        if kra_enabled:
            try:
                kra.install_check(remote_api, config, options)
            except RuntimeError as e:
                raise ScriptError(e)

        ansible_log.debug("-- CHECK DNS --")

        if options.setup_dns:
            dns.install_check(False, remote_api, True, options,
                              config.host_name)
            config.ips = dns.ip_addresses
        else:
            config.ips = installutils.get_server_ip_address(
                config.host_name, not installer.interactive,
                False, options.ip_addresses)

            # check addresses here, dns module is doing own check
            no_matching_interface_for_ip_address_warning(config.ips)

        ansible_log.debug("-- CHECK ADTRUST --")

        if options.setup_adtrust or sid_generation_always:
            adtrust.install_check(False, options, remote_api)

    except errors.ACIError:
        logger.debug("%s", traceback.format_exc())
        ansible_module.fail_json(
            msg=("\nInsufficient privileges to promote the server."
                 "\nPossible issues:"
                 "\n- A user has insufficient privileges"
                 "\n- This client has insufficient privileges "
                 "to become an IPA replica"))
    except errors.LDAPError:
        logger.debug("%s", traceback.format_exc())
        ansible_module.fail_json(msg="\nUnable to connect to LDAP server %s" %
                                 config.master_host_name)
    except ScriptError as e:
        ansible_module.fail_json(msg=str(e))
    finally:
        if replman and replman.conn:
            ansible_log.debug("-- UNBIND REPLMAN--")
            replman.conn.unbind()
        if conn.isconnected():
            ansible_log.debug("-- DISCONNECT --")
            conn.disconnect()

    ansible_log.debug("-- CHECK CONNECTION --")

    # check connection
    if not options.skip_conncheck:
        if add_to_ipaservers:
            # use user's credentials when the server host is not ipaservers
            if installer._ccache is None:
                os.environ.pop('KRB5CCNAME', None)
            else:
                os.environ['KRB5CCNAME'] = installer._ccache

        try:
            with redirect_stdout(ansible_log):
                replica_conn_check(
                    config.master_host_name, config.host_name,
                    config.realm_name, options.setup_ca, 389,
                    options.admin_password, principal=options.principal,
                    ca_cert_file=cafile)
        except ScriptError as e:
            ansible_module.fail_json(msg=str(e))
        finally:
            if add_to_ipaservers:
                os.environ['KRB5CCNAME'] = ccache

    if hasattr(tasks, "configure_pkcs11_modules"):
        if tasks.configure_pkcs11_modules(fstore):
            ansible_log.info("Disabled p11-kit-proxy")

    installer._ca_enabled = ca_enabled
    installer._kra_enabled = kra_enabled
    installer._ca_file = cafile
    installer._fstore = fstore
    installer._sstore = sstore
    installer._config = config
    installer._add_to_ipaservers = add_to_ipaservers

    # done #

    ansible_module.exit_json(
        changed=True,
        ccache=ccache,
        installer_ccache=installer._ccache,
        subject_base=str(config.subject_base),
        forward_policy=options.forward_policy,
        _ca_enabled=ca_enabled,
        _ca_subject=str(options._ca_subject),
        _subject_base=str(options._subject_base) if options._subject_base
        is not None else None,
        _kra_enabled=kra_enabled,
        _ca_file=cafile,
        _top_dir=installer._top_dir,
        _add_to_ipaservers=add_to_ipaservers,
        _dirsrv_pkcs12_info=dirsrv_pkcs12_info,
        _dirsrv_ca_cert=dirsrv_ca_cert,
        _http_pkcs12_info=http_pkcs12_info,
        _http_ca_cert=http_ca_cert,
        _pkinit_pkcs12_info=pkinit_pkcs12_info,
        _pkinit_ca_cert=pkinit_ca_cert,
        _random_serial_numbers=options._random_serial_numbers,
        no_dnssec_validation=options.no_dnssec_validation,
        config_setup_ca=config.setup_ca,
        config_master_host_name=config.master_host_name,
        config_ca_host_name=config.ca_host_name,
        config_kra_host_name=config.kra_host_name,
        config_ips=[str(ip) for ip in config.ips],
        # ad trust
        dns_ip_addresses=[str(ip) for ip in dns.ip_addresses],
        dns_reverse_zones=dns.reverse_zones,
        rid_base=options.rid_base,
        secondary_rid_base=options.secondary_rid_base,
        adtrust_netbios_name=adtrust.netbios_name,
        adtrust_reset_netbios_name=adtrust.reset_netbios_name)


if __name__ == '__main__':
    main()
