From 14f975b41116f636b04b9a999a0ef99f442eb9ff Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 21 Jun 2018 13:18:11 +0200
Subject: [PATCH] ipaserver: Add support for 4.7 (4.6.90-pre2)

With IPA 4.7 bigger changes have been introduced

Changes:
- Use of timeconf and chrony instead of ntpconf and ntpd.
- New IPAChangeConf (not used in ipaserver modules)
- New check_ldap_conf form ipaclient.install.client
- custodia instance needed for ca and kra
- no_ntp defaults to yes for client installation part
- A new option ntp_pool has been introduced (set to None).
---
 module_utils/ansible_ipa_server.py            | 23 +++++++--
 roles/ipaserver/library/ipaserver_prepare.py  |  2 +-
 roles/ipaserver/library/ipaserver_setup_ca.py | 22 +++++++--
 roles/ipaserver/library/ipaserver_setup_ds.py |  2 +-
 .../ipaserver/library/ipaserver_setup_kra.py  |  9 +++-
 .../ipaserver/library/ipaserver_setup_ntp.py  | 28 ++++++++---
 roles/ipaserver/library/ipaserver_test.py     | 48 ++++++++-----------
 roles/ipaserver/tasks/install.yml             | 16 ++++---
 8 files changed, 99 insertions(+), 51 deletions(-)

diff --git a/module_utils/ansible_ipa_server.py b/module_utils/ansible_ipa_server.py
index 8e8f9b5c..d059f3ab 100644
--- a/module_utils/ansible_ipa_server.py
+++ b/module_utils/ansible_ipa_server.py
@@ -51,12 +51,14 @@ if NUM_VERSION >= 40500:
 
     import six
 
+    if NUM_VERSION >= 40690:
+        from ipaclient.install.ipachangeconf import IPAChangeConf
     from ipalib.install import certmonger, sysrestore
     from ipapython import ipautil
     if NUM_VERSION < 40600:
         from ipapython.ipa_log_manager import root_logger
     from ipapython.ipautil import (
-        format_netloc, ipa_generate_password, run, user_input)
+        ipa_generate_password, run, user_input)
     from ipapython.admintool import ScriptError
     from ipaplatform import services
     from ipaplatform.paths import paths
@@ -70,11 +72,21 @@ if NUM_VERSION >= 40500:
         no_matching_interface_for_ip_address_warning,
     )
     from ipapython.dnsutil import check_zone_overlap
-    from ipaclient.install import ntpconf
+    try:
+        from ipaclient.install import timeconf
+        from ipaclient.install.client import sync_time
+        time_service = "chronyd"
+    except ImportError:
+        try:
+            from ipaclient.install import ntpconf as timeconf
+        except ImportError:
+            from ipaclient import ntpconf as timeconf
+        from ipaserver.install import ntpinstance
+        time_service = "ntpd"
     from ipaserver.install import (
         adtrust, bindinstance, ca, dns, dsinstance,
         httpinstance, installutils, kra, krbinstance,
-        ntpinstance, otpdinstance, custodiainstance, replication, service,
+        otpdinstance, custodiainstance, replication, service,
         sysupgrade)
     adtrust_imported = True
     kra_imported = True
@@ -105,6 +117,11 @@ if NUM_VERSION >= 40500:
     except ImportError:
         _server_trust_ad_installed = False
 
+    try:
+        from ipaclient.install.client import check_ldap_conf
+    except ImportError:
+        check_ldap_conf = None
+
 else:
     # IPA version < 4.5
 
diff --git a/roles/ipaserver/library/ipaserver_prepare.py b/roles/ipaserver/library/ipaserver_prepare.py
index 9437ce0d..51ad8b86 100644
--- a/roles/ipaserver/library/ipaserver_prepare.py
+++ b/roles/ipaserver/library/ipaserver_prepare.py
@@ -200,7 +200,7 @@ def main():
     fd.write("realm=%s\n" % options.realm_name)
     fd.write("domain=%s\n" % options.domain_name)
     fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % \
-             format_netloc(options.host_name))
+             ipautil.format_netloc(options.host_name))
     fd.write("ldap_uri=ldapi://%%2fvar%%2frun%%2fslapd-%s.socket\n" % \
              installutils.realm_to_serverid(options.realm_name))
     if options.setup_ca:
diff --git a/roles/ipaserver/library/ipaserver_setup_ca.py b/roles/ipaserver/library/ipaserver_setup_ca.py
index c1ff47a5..c1615471 100644
--- a/roles/ipaserver/library/ipaserver_setup_ca.py
+++ b/roles/ipaserver/library/ipaserver_setup_ca.py
@@ -170,6 +170,8 @@ def main():
 
     # init #################################################################
 
+    options.promote = False  # first master, no promotion
+
     fstore = sysrestore.FileStore(paths.SYSRESTORE)
 
     api_Backend_ldap2(options.host_name, options.setup_ca, connect=True)
@@ -186,6 +188,11 @@ def main():
     # setup CA ##############################################################
 
     with redirect_stdout(ansible_log):
+        if NUM_VERSION >= 40604:
+            custodia = custodiainstance.get_custodia_instance(
+                options, custodiainstance.CustodiaModes.MASTER_PEER)
+            custodia.create_instance()
+
         if options.setup_ca:
             if not options.external_cert_files and options.external_ca:
                 # stage 1 of external CA installation
@@ -193,7 +200,10 @@ def main():
                               if n in options.__dict__}
                 write_cache(cache_vars)
 
-            ca.install_step_0(False, None, options)
+            if NUM_VERSION >= 40604:
+                ca.install_step_0(False, None, options, custodia=custodia)
+            else:
+                ca.install_step_0(False, None, options)
         else:
             # Put the CA cert where other instances expect it
             x509.write_certificate(options._http_ca_cert, paths.IPA_CA_CRT)
@@ -210,13 +220,15 @@ def main():
             x509.write_certificate(options._http_ca_cert, paths.CA_BUNDLE_PEM)
             os.chmod(paths.CA_BUNDLE_PEM, 0o444)
 
-    with redirect_stdout(ansible_log):
         # we now need to enable ssl on the ds
         ds.enable_ssl()
 
-    if options.setup_ca:
-        with redirect_stdout(ansible_log):
-            ca.install_step_1(False, None, options)
+        if options.setup_ca:
+            with redirect_stdout(ansible_log):
+                if NUM_VERSION >= 40604:
+                    ca.install_step_1(False, None, options, custodia=custodia)
+                else:
+                    ca.install_step_1(False, None, options)
 
     ansible_module.exit_json(changed=True)
 
diff --git a/roles/ipaserver/library/ipaserver_setup_ds.py b/roles/ipaserver/library/ipaserver_setup_ds.py
index 062fd39c..f4aa9cba 100644
--- a/roles/ipaserver/library/ipaserver_setup_ds.py
+++ b/roles/ipaserver/library/ipaserver_setup_ds.py
@@ -151,7 +151,7 @@ def main():
                                ca_subject=options.ca_subject,
                                hbac_allow=not options.no_hbac_allow,
                                setup_pkinit=not options.no_pkinit)
-            if not options.dirsrv_cert_files:
+            if not options.dirsrv_cert_files and NUM_VERSION < 40690:
                 ntpinstance.ntp_ldap_enable(options.host_name, ds.suffix,
                                             options.realm_name)
 
diff --git a/roles/ipaserver/library/ipaserver_setup_kra.py b/roles/ipaserver/library/ipaserver_setup_kra.py
index 3958d7aa..2982a73c 100644
--- a/roles/ipaserver/library/ipaserver_setup_kra.py
+++ b/roles/ipaserver/library/ipaserver_setup_kra.py
@@ -80,7 +80,14 @@ def main():
     # setup kra #####################################################
 
     with redirect_stdout(ansible_log):
-        kra.install(api, None, options)
+        if NUM_VERSION >= 40604:
+            custodia = custodiainstance.get_custodia_instance(
+                options, custodiainstance.CustodiaModes.MASTER_PEER)
+            custodia.create_instance()
+
+            kra.install(api, None, options, custodia=custodia)
+        else:
+            kra.install(api, None, options)
 
     # done ##########################################################
 
diff --git a/roles/ipaserver/library/ipaserver_setup_ntp.py b/roles/ipaserver/library/ipaserver_setup_ntp.py
index 3de34ca9..9626c5bb 100644
--- a/roles/ipaserver/library/ipaserver_setup_ntp.py
+++ b/roles/ipaserver/library/ipaserver_setup_ntp.py
@@ -64,12 +64,28 @@ def main():
 
     # setup NTP #####################################################
 
-    ntpconf.force_ntpd(sstore)
-    ntp = ntpinstance.NTPInstance(fstore)
-    ntp.set_output(ansible_log)
-    with redirect_stdout(ansible_log):
-        if not ntp.is_configured():
-            ntp.create_instance()
+    if time_service == "chronyd":
+        # We have to sync time before certificate handling on master.
+        # As chrony configuration is moved from client here, unconfiguration of
+        # chrony will be handled here in uninstall() method as well by invoking
+        # the ipa-server-install --uninstall
+        ansible_module.log("Synchronizing time")
+        options.ntp_servers = None
+        options.ntp_pool = None
+        if sync_time(options, fstore, sstore):
+            ansible_module.log("Time synchronization was successful.")
+        else:
+            ansible_module.warn("IPA was unable to sync time with chrony!")
+            ansible_module.warn("Time synchronization is required for IPA "
+                                "to work correctly")
+    else:
+        # Configure ntpd
+        timeconf.force_ntpd(sstore)
+        ntp = ntpinstance.NTPInstance(fstore)
+        ntp.set_output(ansible_log)
+        with redirect_stdout(ansible_log):
+            if not ntp.is_configured():
+                ntp.create_instance()
 
     # done ##########################################################
 
diff --git a/roles/ipaserver/library/ipaserver_test.py b/roles/ipaserver/library/ipaserver_test.py
index 975df0da..a519e63d 100644
--- a/roles/ipaserver/library/ipaserver_test.py
+++ b/roles/ipaserver/library/ipaserver_test.py
@@ -506,6 +506,8 @@ def main():
 
     tasks.check_ipv6_stack_enabled()
     tasks.check_selinux_status()
+    if check_ldap_conf is not None:
+        check_ldap_conf()
 
     _installation_cleanup = True
     if not options.external_ca and not options.external_cert_files and \
@@ -515,17 +517,18 @@ def main():
 
     if not options.no_ntp:
         try:
-            ntpconf.check_timedate_services()
-        except ntpconf.NTPConflictingService as e:
+            timeconf.check_timedate_services()
+        except timeconf.NTPConflictingService as e:
             ansible_module.log("Conflicting time&date synchronization service '%s'"
-                       " will be disabled in favor of ntpd" % \
-                       e.conflicting_service)
-        except ntpconf.NTPConfigurationError:
+                       " will be disabled in favor of %s" % \
+                       (e.conflicting_service, time_service))
+        except timeconf.NTPConfigurationError:
             pass
 
-    # Check to see if httpd is already configured to listen on 443
-    if httpinstance.httpd_443_configured():
-        ansible_module.fail_json(msg="httpd is already configured to listen on 443.")
+    if hasattr(httpinstance, "httpd_443_configured"):
+        # Check to see if httpd is already configured to listen on 443
+        if httpinstance.httpd_443_configured():
+            ansible_module.fail_json(msg="httpd is already configured to listen on 443.")
 
     if not options.external_cert_files:
         # Make sure the 389-ds ports are available
@@ -534,20 +537,6 @@ def main():
         except ScriptError as e:
             ansible_module.fail_json(msg=e)
 
-    if not options.no_ntp:
-        try:
-            ntpconf.check_timedate_services()
-        except ntpconf.NTPConflictingService as e:
-            ansible_module.warn(
-                "Conflicting time&date synchronization service "
-                "'%s' will be disabled" % e.conflicting_service)
-        except ntpconf.NTPConfigurationError:
-            pass
-
-    # Check to see if httpd is already configured to listen on 443
-    if httpinstance.httpd_443_configured():
-        ansible_module.fail_json(msg="httpd is already configured to listen on 443.")
-
     # check bind packages are installed
     if options.setup_dns:
         # Don't require an external DNS to say who we are if we are
@@ -560,12 +549,9 @@ def main():
     else:
         options.host_default = get_fqdn()
 
-    _host_name_overridden = False
     try:
         verify_fqdn(options.host_default, options.no_host_dns)
         options.host_name = options.host_default
-        if options.host_default != get_fqdn():
-            _host_name_overridden = True
     except BadHostError as e:
         ansible_module.fail_json(msg=e)
     options.host_name = options.host_name.lower()
@@ -581,6 +567,11 @@ def main():
     if not options.realm_name:
         options.realm_name = options.domain_name
     options.realm_name = options.realm_name.upper()
+    if NUM_VERSION >= 40690:
+        try:
+            validate_domain_name(options.realm_name, entity="realm")
+        except ValueError as e:
+            raise ScriptError("Invalid realm name: {}".format(unicode(e)))
 
     if not options.setup_adtrust:
         # If domain name and realm does not match, IPA server will not be able
@@ -686,7 +677,7 @@ def main():
         fd.write("basedn=%s\n" % ipautil.realm_to_suffix(options.realm_name))
         fd.write("realm=%s\n" % options.realm_name)
         fd.write("domain=%s\n" % options.domain_name)
-        fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % format_netloc(options.host_name))
+        fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % ipautil.format_netloc(options.host_name))
         fd.write("ldap_uri=ldapi://%%2fvar%%2frun%%2fslapd-%s.socket\n" %
                  installutils.realm_to_serverid(options.realm_name))
         if options.setup_ca:
@@ -744,6 +735,9 @@ def main():
         except OSError:
             ansible_module.fail_json(msg="Could not remove %s" % ipa_tempdir)
 
+    # Always set _host_name_overridden
+    options._host_name_overridden = bool(options.host_name)
+
     # done ##################################################################
 
     ansible_module.exit_json(changed=True,
@@ -753,7 +747,7 @@ def main():
                              realm=options.realm_name,
                              ip_addresses=[ str(ip) for ip in ip_addresses ],
                              hostname=options.host_name,
-                             _hostname_overridden=_host_name_overridden,
+                             _hostname_overridden=options._host_name_overridden,
                              no_host_dns=options.no_host_dns,
                              ### server ###
                              setup_adtrust=options.setup_adtrust,
diff --git a/roles/ipaserver/tasks/install.yml b/roles/ipaserver/tasks/install.yml
index 1bfaf6e0..d88f7994 100644
--- a/roles/ipaserver/tasks/install.yml
+++ b/roles/ipaserver/tasks/install.yml
@@ -125,8 +125,9 @@
       setup_adtrust: "{{ result_ipaserver_test.setup_adtrust }}"
       setup_kra: "{{ result_ipaserver_test.setup_kra }}"
       setup_dns: "{{ ipaserver_setup_dns }}"
-      #no_pkinit: "{{ result_ipaserver_test.no_pkinit }}"
       ### certificate system ###
+      # external_ca
+      # external_cert_files
       subject_base: "{{ result_ipaserver_test.subject_base }}"
       ca_subject: "{{ result_ipaserver_test.ca_subject }}"
       ### dns ###
@@ -138,6 +139,11 @@
       no_forwarders: "{{ ipaserver_no_forwarders }}"
       auto_forwarders: "{{ ipaserver_auto_forwarders }}"
       no_dnssec_validation: "{{ result_ipaserver_test.no_dnssec_validation }}"
+      ### ad trust ###
+      # enable_compat
+      # netbios_name
+      # rid_base
+      # secondary_rid_base
       ### additional ###
       setup_ca: "{{ result_ipaserver_test.setup_ca }}"
       _hostname_overridden: "{{ result_ipaserver_test._hostname_overridden }}"
@@ -329,13 +335,9 @@
       ipaclient_on_master: yes
       ipaclient_domain: "{{ result_ipaserver_test.domain }}"
       ipaclient_realm: "{{ result_ipaserver_test.realm }}"
-      ipaclient_server: "{{ result_ipaserver_test.hostname }}"
+      ipaclient_servers: [ "{{ result_ipaserver_test.hostname }}" ]
       ipaclient_hostname: "{{ result_ipaserver_test.hostname }}"
-      #ipaclient_no_dns_sshfp: "{{ ipaclient_no_dns_sshfp }}"
-      #ipaclient_ssh_trust_dns: "{{ ipaclient_ssh_trust_dns }}"
-      #ipaclient_no_ssh: "{{ ipaclient_no_ssh }}"
-      #ipaclient_no_sshd: "{{ ipaclient_no_sshd }}"
-      #ipaclient_mkhomedir: "{{ ipaclient_mkhomedir }}"
+      ipaclient_no_ntp: "{{ 'true' if result_ipaserver_test.ipa_python_version >= 40690 else 'false' }}"
 
   #- name: Install - Setup client
   #  command: >
-- 
GitLab