ipaclient_test: More tests from ipaclient installer, updated ansible_ipa_client

The ipaclient_test module was not doing all tests that should be done
with the provided settings. All tests from ipaclient installer parts
are now part of ipaclient_test. There are some minor changes though to
make sure that the repair mode could still be used.

ansible_ipa_client bindings have been updated to fullfill new needs.
This commit is contained in:
Thomas Woerner
2019-03-25 13:38:45 +01:00
parent bf1f8bff5c
commit c5ce754850
4 changed files with 683 additions and 290 deletions

View File

@@ -12,8 +12,12 @@ ipaclient_no_dns_lookup: no
ipaclient_ssh_trust_dns: no
ipaclient_no_ssh: no
ipaclient_no_sshd: no
#ipaclient_no_dns_sshfp: no
#ipaclient_force: no
ipaclient_force_ntpd: no
ipaclient_no_nisdomain: no
ipaclient_configure_firefox: no
ipahost_all_ip_addresses: no
### packages ###
ipaclient_install_packages: yes

View File

@@ -49,15 +49,6 @@ options:
hostname:
description: The hostname of the machine to join (FQDN).
required: false
ca_cert_file:
description: A CA certificate to use.
required: false
on_master:
description: IPA client installation on IPA server
required: false
default: false
type: bool
default: no
ntp_servers:
description: List of NTP servers to use
required: false
@@ -72,14 +63,56 @@ options:
default: false
type: bool
default: no
no_nisdomain:
description: Do not configure NIS domain name
required: false
force_ntpd:
description: Stop and disable any time&date synchronization services besides ntpd. Deprecated since 4.7.
requried: false
type: bool
default: no
nisdomain:
description: NIS domain name
required: false
no_nisdomain:
description: Do not configure NIS domain name
required: false
type: bool
default: no
kinit_attempts:
description: Repeat the request for host Kerberos ticket X times.
required: false
type: int
default: 5
ca_cert_files:
description: CA certificates to use.
required: false
configure_firefox:
description: Configure Firefox to use IPA domain credentials
required: false
type: bool
default: no
firefox_dir:
description: Specify directory where Firefox is installed (for example: '/usr/lib/firefox')
required: false
ip_addresses:
description: All routable IP addresses configured on any interface will be added to DNS.
required: false
type: bool
default: no
all_ip_addresses:
description: All routable IP addresses configured on any interface will be added to DNS.
required: false
type: bool
default: no
on_master:
description: IPA client installation on IPA server
required: false
default: false
type: bool
default: no
enable_dns_updates:
description: Configures the machine to attempt dns updates when the ip address changes.
required: false
type: bool
default: no
author:
- Thomas Woerner
'''
@@ -227,73 +260,249 @@ def get_ipa_conf():
def main():
module = AnsibleModule(
argument_spec = dict(
servers=dict(required=False, type='list', default=[]),
domain=dict(required=False),
realm=dict(required=False),
hostname=dict(required=False),
ca_cert_file=dict(required=False),
on_master=dict(required=False, type='bool', default=False),
ntp_servers=dict(required=False, type='list', default=[]),
ntp_pool=dict(required=False),
### basic ###
domain=dict(required=False, default=None),
servers=dict(required=False, type='list', default=None),
realm=dict(required=False, default=None),
hostname=dict(required=False, default=None),
ntp_servers=dict(required=False, type='list', default=None),
ntp_pool=dict(required=False, default=None),
no_ntp=dict(required=False, type='bool', default=False),
#no_nisdomain=dict(required=False, type='bool', default='no'),
#nisdomain=dict(required=False),
force_ntpd=dict(required=False, type='bool', default=False),
nisdomain=dict(required=False, default=None),
no_nisdomain=dict(required=False, type='bool', default='no'),
kinit_attempts=dict(required=False, type='int'),
ca_cert_files=dict(required=False, type='list', default=None),
configure_firefox=dict(required=False, type='bool', default=False),
firefox_dir=dict(required=False),
ip_addresses=dict(required=False, type='list', default=None),
all_ip_addresses=dict(required=False, type='bool', default=False),
on_master=dict(required=False, type='bool', default=False),
### sssd ###
enable_dns_updates=dict(required=False, type='bool', default=False),
),
supports_check_mode = True,
)
module._ansible_debug = True
options.domain = module.params.get('domain')
#module._ansible_debug = True
options.domain_name = module.params.get('domain')
options.servers = module.params.get('servers')
options.realm = module.params.get('realm')
options.hostname = module.params.get('hostname')
options.ca_cert_file = module.params.get('ca_cert_file')
options.on_master = module.params.get('on_master')
options.realm_name = module.params.get('realm')
options.host_name = module.params.get('hostname')
options.ntp_servers = module.params.get('ntp_servers')
options.ntp_pool = module.params.get('ntp_pool')
options.no_ntp = module.params.get('no_ntp')
options.conf_ntp = not options.no_ntp
#options.no_nisdomain = module.params.get('no_nisdomain')
#options.nisdomain = module.params.get('nisdomain')
#options.ip_addresses
#options.all_ip_addresses
#options.enable_dns_updates
options.force_ntpd = module.params.get('force_ntpd')
options.nisdomain = module.params.get('nisdomain')
options.no_nisdomain = module.params.get('no_nisdomain')
options.kinit_attempts = module.params.get('kinit_attempts')
options.ca_cert_files = module.params.get('ca_cert_files')
options.configure_firefox = module.params.get('configure_firefox')
options.firefox_dir = module.params.get('firefox_dir')
options.ip_addresses = module.params.get('ip_addresses')
options.all_ip_addresses = module.params.get('all_ip_addresses')
options.on_master = module.params.get('on_master')
options.enable_dns_updates = module.params.get('enable_dns_updates')
# Get domain from first server if domain is not set, but if there are
# servers
if options.domain_name is None and len(options.servers) > 0:
options.domain_name = options.servers[0][options.servers[0].find(".")+1:]
try:
self = options
### HostNameInstallInterface ###
if options.ip_addresses is not None:
for value in options.ip_addresses:
try:
CheckedIPAddress(value)
except Exception as e:
raise ValueError("invalid IP address {0}: {1}".format(
value, e))
### ServiceInstallInterface ###
validate_domain_name(options.domain_name)
if options.realm_name:
validate_domain_name(options.realm_name, entity="realm")
### ClientInstallInterface ###
if options.kinit_attempts < 1:
raise ValueError("expects an integer greater than 0.")
### ClientInstallInterface.__init__ ###
if self.servers and not self.domain_name:
raise RuntimeError(
"--server cannot be used without providing --domain")
if self.force_ntpd:
logger.warning("Option --force-ntpd has been deprecated")
if self.ntp_servers and self.no_ntp:
raise RuntimeError(
"--ntp-server cannot be used together with --no-ntp")
if self.ntp_pool and self.no_ntp:
raise RuntimeError(
"--ntp-pool cannot be used together with --no-ntp")
if self.no_nisdomain and self.nisdomain:
raise RuntimeError(
"--no-nisdomain cannot be used together with --nisdomain")
if self.ip_addresses:
if self.enable_dns_updates:
raise RuntimeError(
"--ip-address cannot be used together with"
" --enable-dns-updates")
if self.all_ip_addresses:
raise RuntimeError(
"--ip-address cannot be used together with"
"--all-ip-addresses")
### SSSDInstallInterface ###
self.no_sssd = False
### ClientInstall ###
if options.ca_cert_files is not None:
for value in options.ca_cert_files:
if not isinstance(value, list):
raise ValueError("Expected list, got {!r}".format(value))
# this is what init() does
value = value[-1]
if not os.path.exists(value):
raise ValueError("'%s' does not exist" % value)
if not os.path.isfile(value):
raise ValueError("'%s' is not a file" % value)
if not os.path.isabs(value):
raise ValueError("'%s' is not an absolute file path" % value)
try:
x509.load_certificate_from_file(value)
except Exception:
raise ValueError("'%s' is not a valid certificate file" % value)
#self.prompt_password = self.interactive
self.no_ac = False
### ClientInstall.__init__ ###
if self.firefox_dir and not self.configure_firefox:
raise RuntimeError(
"--firefox-dir cannot be used without --configure-firefox "
"option")
except (RuntimeError, ValueError) as e:
module.fail_json(msg=str(e))
### ipaclient.install.client.init ###
# root_logger
options.debug = False
options.unattended = not installer.interactive
if options.domain_name:
options.domain = normalize_hostname(installer.domain_name)
else:
options.domain = None
options.server = options.servers
options.realm = options.realm_name
#installer.primary = installer.fixed_primary
#if installer.principal:
# installer.password = installer.admin_password
#else:
# installer.password = installer.host_password
installer.hostname = installer.host_name
options.conf_ntp = not options.no_ntp
#installer.trust_sshfp = installer.ssh_trust_dns
#installer.conf_ssh = not installer.no_ssh
#installer.conf_sshd = not installer.no_sshd
#installer.conf_sudo = not installer.no_sudo
#installer.create_sshfp = not installer.no_dns_sshfp
if installer.ca_cert_files:
installer.ca_cert_file = installer.ca_cert_files[-1]
else:
installer.ca_cert_file = None
#installer.location = installer.automount_location
installer.dns_updates = installer.enable_dns_updates
#installer.krb5_offline_passwords = not installer.no_krb5_offline_passwords
installer.sssd = not installer.no_sssd
try:
### client ###
# global variables
hostname = None
hostname_source = None
nosssd_files = None
dnsok = False
cli_domain = None
cli_server = None
subject_base = None
cli_realm = None
cli_kdc = None
client_domain = None
cli_basedn = None
# end of global variables
### client.install_check ###
logger.info("This program will set up FreeIPA client.")
logger.info("Version {}".format(version.VERSION))
logger.info("")
cli_domain_source = 'Unknown source'
cli_server_source = 'Unknown source'
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
if options.ntp_servers and options.no_ntp:
module.fail_json(
msg="--ntp-server cannot be used together with --no-ntp")
if not os.getegid() == 0:
raise ScriptError(
"You must be root to run ipa-client-install.",
rval=CLIENT_INSTALL_ERROR)
if options.ntp_pool and options.no_ntp:
module.fail_json(
msg="--ntp-pool cannot be used together with --no-ntp")
tasks.check_selinux_status()
#if options.no_nisdomain and options.nisdomain:
# module.fail_json(
# "--no-nisdomain cannot be used together with --nisdomain")
#if is_ipa_client_installed(fstore, on_master=options.on_master):
# logger.error("IPA client is already configured on this system.")
# logger.info(
# "If you want to reinstall the IPA client, uninstall it first "
# "using 'ipa-client-install --uninstall'.")
# raise ScriptError(rval=CLIENT_ALREADY_CONFIGURED)
#if options.ip_addresses:
# if options.enable_dns_updates:
# module.fail_json(
# "--ip-addresses cannot be used together with"
# " --enable-dns-updates")
check_ldap_conf()
# if options.all_ip_addresses:
# module.fail_json(
# "--ip-address cannot be used together with"
# "--all-ip-addresses")
if options.conf_ntp:
try:
timeconf.check_timedate_services()
except timeconf.NTPConflictingService as e:
logger.info("WARNING: conflicting time&date synchronization service '{}'"
" will be disabled".format(e.conflicting_service))
logger.info("in favor of chronyd")
logger.info("")
except timeconf.NTPConfigurationError:
pass
# password, principal and keytab are checked in tasks/install.yml
#if options.unattended and (
# options.password is None and
# options.principal is None and
# options.keytab is None and
# options.prompt_password is False and
# not options.on_master
#):
# raise ScriptError(
# "One of password / principal / keytab is required.",
# rval=CLIENT_INSTALL_ERROR)
if options.hostname:
hostname = options.hostname
@@ -302,62 +511,150 @@ def main():
hostname = socket.getfqdn()
hostname_source = "Machine's FQDN"
if hostname != hostname.lower():
module.fail_json(
msg="Invalid hostname '%s', must be lower-case." % hostname)
raise ScriptError(
"Invalid hostname '{}', must be lower-case.".format(hostname),
rval=CLIENT_INSTALL_ERROR
)
if (hostname == 'localhost') or (hostname == 'localhost.localdomain'):
module.fail_json(
msg="Invalid hostname, '%s' must not be used." % hostname)
if hostname in ('localhost', 'localhost.localdomain'):
raise ScriptError(
"Invalid hostname, '{}' must not be used.".format(hostname),
rval=CLIENT_INSTALL_ERROR)
# Get domain from first server if domain is not set, but there are servers
if options.domain is None and len(options.servers) > 0:
options.domain = options.servers[0][options.servers[0].find(".")+1:]
# --no-sssd is not supported any more for rhel-based distros
if not tasks.is_nosssd_supported() and not options.sssd:
raise ScriptError(
"Option '--no-sssd' is incompatible with the 'authselect' tool "
"provided by this distribution for configuring system "
"authentication resources",
rval=CLIENT_INSTALL_ERROR)
# --noac is not supported any more for rhel-based distros
if not tasks.is_nosssd_supported() and options.no_ac:
raise ScriptError(
"Option '--noac' is incompatible with the 'authselect' tool "
"provided by this distribution for configuring system "
"authentication resources",
rval=CLIENT_INSTALL_ERROR)
# when installing with '--no-sssd' option, check whether nss-ldap is
# installed
if not options.sssd:
if not os.path.exists(paths.PAM_KRB5_SO):
raise ScriptError(
"The pam_krb5 package must be installed",
rval=CLIENT_INSTALL_ERROR)
(nssldap_installed, nosssd_files) = nssldap_exists()
if not nssldap_installed:
raise ScriptError(
"One of these packages must be installed: nss_ldap or "
"nss-pam-ldapd",
rval=CLIENT_INSTALL_ERROR)
# principal and keytab are checked in tasks/install.yml
#if options.keytab and options.principal:
# raise ScriptError(
# "Options 'principal' and 'keytab' cannot be used together.",
# rval=CLIENT_INSTALL_ERROR)
# keytab and force_join are checked in tasks/install.yml
#if options.keytab and options.force_join:
# logger.warning("Option 'force-join' has no additional effect "
# "when used with together with option 'keytab'.")
# Added with freeipa-4.7.1 >>>
# Remove invalid keytab file
try:
gssapi.Credentials(
store={'keytab': paths.KRB5_KEYTAB},
usage='accept',
)
except gssapi.exceptions.GSSError:
logger.debug("Deleting invalid keytab: '%s'.", paths.KRB5_KEYTAB)
remove_file(paths.KRB5_KEYTAB)
# Added with freeipa-4.7.1 <<<
# Check if old certificate exist and show warning
if (
not options.ca_cert_file and
get_cert_path(options.ca_cert_file) == paths.IPA_CA_CRT
):
logger.warning("Using existing certificate '%s'.", paths.IPA_CA_CRT)
if not check_ip_addresses(options):
module.warn("Failed to check ip addresses, check installation log")
raise ScriptError(rval=CLIENT_INSTALL_ERROR)
# Create the discovery instance
ds = ipadiscovery.IPADiscovery()
ret = ds.search(
domain=options.domain,
servers=options.servers,
realm=options.realm,
servers=options.server,
realm=options.realm_name,
hostname=hostname,
ca_cert_path=get_cert_path(options.ca_cert_file))
ca_cert_path=get_cert_path(options.ca_cert_file)
)
if options.servers and ret != 0:
if options.server and ret != 0:
# There is no point to continue with installation as server list was
# passed as a fixed list of server and thus we cannot discover any
# better result
module.fail_json(msg="Failed to verify that %s is an IPA Server." % \
', '.join(options.servers))
logger.error(
"Failed to verify that %s is an IPA Server.",
', '.join(options.server))
logger.error(
"This may mean that the remote server is not up "
"or is not reachable due to network or firewall settings.")
print_port_conf_info()
module.warn("Failed to verify that %s is an IPA Server." %
', '.join(options.server))
raise ScriptError(rval=CLIENT_INSTALL_ERROR)
if ret == ipadiscovery.BAD_HOST_CONFIG:
module.fail_json(msg="Can't get the fully qualified name of this host")
logger.error("Can't get the fully qualified name of this host")
logger.info("Check that the client is properly configured")
module.warn("Can't get the fully qualified name of this host")
raise ScriptError(rval=CLIENT_INSTALL_ERROR)
if ret == ipadiscovery.NOT_FQDN:
module.fail_json(msg="%s is not a fully-qualified hostname" % hostname)
raise ScriptError(
"{} is not a fully-qualified hostname".format(hostname),
rval=CLIENT_INSTALL_ERROR)
if ret in (ipadiscovery.NO_LDAP_SERVER, ipadiscovery.NOT_IPA_SERVER) \
or not ds.domain:
if ret == ipadiscovery.NO_LDAP_SERVER:
if ds.server:
module.log("%s is not an LDAP server" % ds.server)
logger.debug("%s is not an LDAP server", ds.server)
else:
module.log("No LDAP server found")
logger.debug("No LDAP server found")
elif ret == ipadiscovery.NOT_IPA_SERVER:
if ds.server:
module.log("%s is not an IPA server" % ds.server)
logger.debug("%s is not an IPA server", ds.server)
else:
module.log("No IPA server found")
logger.debug("No IPA server found")
else:
module.log("Domain not found")
logger.debug("Domain not found")
if options.domain:
cli_domain = options.domain
cli_domain_source = 'Provided as option'
elif options.unattended:
raise ScriptError(
"Unable to discover domain, not provided on command line",
rval=CLIENT_INSTALL_ERROR)
else:
module.fail_json(
msg="Unable to discover domain, not provided")
raise ScriptError("No interactive installation")
# logger.info(
# "DNS discovery failed to determine your DNS domain")
# cli_domain = user_input(
# "Provide the domain name of your IPA server (ex: example.com)",
# allow_empty=False)
# cli_domain_source = 'Provided interactively'
# logger.debug(
# "will use interactively provided domain: %s", cli_domain)
ret = ds.search(
domain=cli_domain,
servers=options.servers,
servers=options.server,
hostname=hostname,
ca_cert_path=get_cert_path(options.ca_cert_file))
@@ -365,19 +662,31 @@ def main():
if ds.domain:
cli_domain = ds.domain
cli_domain_source = ds.domain_source
module.debug("will use discovered domain: %s" % cli_domain)
logger.debug("will use discovered domain: %s", cli_domain)
client_domain = hostname[hostname.find(".")+1:]
if ret in (ipadiscovery.NO_LDAP_SERVER, ipadiscovery.NOT_IPA_SERVER) \
or not ds.server:
module.debug("IPA Server not found")
if options.servers:
cli_server = options.servers
logger.debug("IPA Server not found")
if options.server:
cli_server = options.server
cli_server_source = 'Provided as option'
elif options.unattended:
raise ScriptError(
"Unable to find IPA Server to join",
rval=CLIENT_INSTALL_ERROR)
else:
module.fail_json(msg="Unable to find IPA Server to join")
raise ScriptError("No interactive installation")
# logger.debug("DNS discovery failed to find the IPA Server")
# cli_server = [
# user_input(
# "Provide your IPA server name (ex: ipa.example.com)",
# allow_empty=False)
# ]
# cli_server_source = 'Provided interactively'
# logger.debug(
# "will use interactively provided server: %s", cli_server[0])
ret = ds.search(
domain=cli_domain,
servers=cli_server,
@@ -387,78 +696,115 @@ def main():
else:
# Only set dnsok to True if we were not passed in one or more servers
# and if DNS discovery actually worked.
if not options.servers:
if not options.server:
(server, domain) = ds.check_domain(
ds.domain, set(), "Validating DNS Discovery")
if server and domain:
module.debug("DNS validated, enabling discovery")
logger.debug("DNS validated, enabling discovery")
dnsok = True
else:
module.debug("DNS discovery failed, disabling discovery")
logger.debug("DNS discovery failed, disabling discovery")
else:
module.debug(
logger.debug(
"Using servers from command line, disabling DNS discovery")
if not cli_server:
if options.servers:
if options.server:
cli_server = ds.servers
cli_server_source = 'Provided as option'
module.debug(
"will use provided server: %s" % ', '.join(options.servers))
logger.debug(
"will use provided server: %s", ', '.join(options.server))
elif ds.server:
cli_server = ds.servers
cli_server_source = ds.server_source
module.debug("will use discovered server: %s" % cli_server[0])
logger.debug("will use discovered server: %s", cli_server[0])
if ret == ipadiscovery.NOT_IPA_SERVER:
module.fail_json(msg="%s is not an IPA v2 Server." % cli_server[0])
logger.error("%s is not an IPA v2 Server.", cli_server[0])
print_port_conf_info()
logger.debug("(%s: %s)", cli_server[0], cli_server_source)
raise ScriptError(rval=CLIENT_INSTALL_ERROR)
if ret == ipadiscovery.NO_ACCESS_TO_LDAP:
module.warn("Anonymous access to the LDAP server is disabled.")
logger.warning("Anonymous access to the LDAP server is disabled.")
logger.info("Proceeding without strict verification.")
logger.info(
"Note: This is not an error if anonymous access "
"has been explicitly restricted.")
ret = 0
if ret == ipadiscovery.NO_TLS_LDAP:
module.warn(
logger.warning(
"The LDAP server requires TLS is but we do not have the CA.")
logger.info("Proceeding without strict verification.")
ret = 0
if ret != 0:
module.fail_json(
msg="Failed to verify that %s is an IPA Server." % cli_server[0])
logger.error(
"Failed to verify that %s is an IPA Server.",
cli_server[0])
logger.error(
"This may mean that the remote server is not up "
"or is not reachable due to network or firewall settings.")
print_port_conf_info()
logger.debug("(%s: %s)", cli_server[0], cli_server_source)
raise ScriptError(rval=CLIENT_INSTALL_ERROR)
cli_kdc = ds.kdc
if dnsok and not cli_kdc:
module.fail_json(
msg="DNS domain '%s' is not configured for automatic "
"KDC address lookup." % ds.realm.lower())
logger.error(
"DNS domain '%s' is not configured for automatic "
"KDC address lookup.", ds.realm.lower())
logger.debug("(%s: %s)", ds.realm, ds.realm_source)
logger.error("KDC address will be set to fixed value.")
if dnsok:
module.log("Discovery was successful!")
logger.info("Discovery was successful!")
elif not options.unattended:
raise ScriptError("No interactive installation")
# if not options.server:
# logger.warning(
# "The failure to use DNS to find your IPA "
# "server indicates that your resolv.conf file is not properly "
# "configured.")
# logger.info(
# "Autodiscovery of servers for failover cannot work "
# "with this configuration.")
# logger.info(
# "If you proceed with the installation, services "
# "will be configured to always access the discovered server for "
# "all operations and will not fail over to other servers in case "
# "of failure.")
# if not user_input(
# "Proceed with fixed values and no DNS discovery?", False):
# raise ScriptError(rval=CLIENT_INSTALL_ERROR)
cli_realm = ds.realm
cli_realm_source = ds.realm_source
module.debug("will use discovered realm: %s" % cli_realm)
logger.debug("will use discovered realm: %s", cli_realm)
if options.realm and options.realm != cli_realm:
module.fail_json(
msg=
"The provided realm name [%s] does not match discovered one [%s]" %
(options.realm, cli_realm))
if options.realm_name and options.realm_name != cli_realm:
logger.error(
"The provided realm name [%s] does not match discovered one [%s]",
options.realm_name, cli_realm)
logger.debug("(%s: %s)", cli_realm, cli_realm_source)
raise ScriptError(rval=CLIENT_INSTALL_ERROR)
cli_basedn = str(ds.basedn)
cli_basedn = ds.basedn
cli_basedn_source = ds.basedn_source
module.debug("will use discovered basedn: %s" % cli_basedn)
logger.debug("will use discovered basedn: %s", cli_basedn)
subject_base = DN(('O', cli_realm))
module.log("Client hostname: %s" % hostname)
module.debug("Hostname source: %s" % hostname_source)
module.log("Realm: %s" % cli_realm)
module.debug("Realm source: %s" % cli_realm_source)
module.log("DNS Domain: %s" % cli_domain)
module.debug("DNS Domain source: %s" % cli_domain_source)
module.log("IPA Server: %s" % ', '.join(cli_server))
module.debug("IPA Server source: %s" % cli_server_source)
module.log("BaseDN: %s" % cli_basedn)
module.debug("BaseDN source: %s" % cli_basedn_source)
logger.info("Client hostname: %s", hostname)
logger.debug("Hostname source: %s", hostname_source)
logger.info("Realm: %s", cli_realm)
logger.debug("Realm source: %s", cli_realm_source)
logger.info("DNS Domain: %s", cli_domain)
logger.debug("DNS Domain source: %s", cli_domain_source)
logger.info("IPA Server: %s", ', '.join(cli_server))
logger.debug("IPA Server source: %s", cli_server_source)
logger.info("BaseDN: %s", cli_basedn)
logger.debug("BaseDN source: %s", cli_basedn_source)
# ipa-join would fail with IP address instead of a FQDN
for srv in cli_server:
@@ -473,55 +819,32 @@ def main():
is_ipaddr = False
if is_ipaddr:
module.warn(
logger.info()
logger.warning(
"It seems that you are using an IP address "
"instead of FQDN as an argument to --server. The "
"installation may fail.")
break
ntp_servers = [ ]
if sync_time is not None:
if options.conf_ntp:
# Attempt to configure and sync time with NTP server (chrony).
sync_time(options, fstore, statestore)
elif options.on_master:
# If we're on master skipping the time sync here because it was done
# in ipa-server-install
logger.info("Skipping attempt to configure and synchronize time with"
" chrony server as it has been already done on master.")
else:
logger.info("Skipping chrony configuration")
#logger.info()
#if not options.unattended and not user_input(
# "Continue to configure the system with these values?", False):
# raise ScriptError(rval=CLIENT_INSTALL_ERROR)
elif not options.on_master and options.conf_ntp:
# Attempt to sync time with IPA server.
# If we're skipping NTP configuration, we also skip the time sync here.
# We assume that NTP servers are discoverable through SRV records
# in the DNS.
# If that fails, we try to sync directly with IPA server,
# assuming it runs NTP
if len(options.ntp_servers) < 1:
# Detect NTP servers
ds = ipadiscovery.IPADiscovery()
ntp_servers = ds.ipadns_search_srv(cli_domain, '_ntp._udp',
None, break_on_first=False)
else:
ntp_servers = options.ntp_servers
except ScriptError as e:
module.warn("2nd part: %s" % e)
module.fail_json(msg=str(e))
# Attempt to sync time:
# At first with given or dicovered time servers. If no ntp
# servers have been given or discovered, then with the ipa
# server.
module.log('Synchronizing time ...')
synced_ntp = False
# use user specified NTP servers if there are any
for s in ntp_servers:
synced_ntp = timeconf.synconce_ntp(s, False)
if synced_ntp:
break
if not synced_ntp and not ntp_servers:
synced_ntp = timeconf.synconce_ntp(cli_server[0], False)
if not synced_ntp:
module.warn("Unable to sync time with NTP server")
#########################################################################
### client._install ###
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
# May not happen in here at this time
#if not options.on_master:
# # Try removing old principals from the keytab
# purge_host_keytab(cli_realm)
# Check if ipa client is already configured
if is_client_configured():
@@ -535,16 +858,16 @@ def main():
"with a conflicting realm")
# Done
module.exit_json(changed=True,
module.exit_json(changed=False,
servers=cli_server,
domain=cli_domain,
realm=cli_realm,
kdc=cli_kdc,
basedn=cli_basedn,
basedn=str(cli_basedn),
hostname=hostname,
client_domain=client_domain,
dnsok=dnsok,
ntp_servers=ntp_servers,
sssd=options.sssd,
ipa_python_version=IPA_PYTHON_VERSION)
if __name__ == '__main__':

View File

@@ -31,19 +31,51 @@ if NUM_VERSION < 30201:
else:
IPA_PYTHON_VERSION = NUM_VERSION
class options_obj(object):
class installer_obj(object):
def __init__(self):
pass
options = options_obj()
def set_logger(self, logger):
self.logger = logger
#def __getattribute__(self, attr):
# value = super(installer_obj, self).__getattribute__(attr)
# if not attr.startswith("--") and not attr.endswith("--"):
# logger.debug(
# " <-- Accessing installer.%s (%s)" % (attr, repr(value)))
# return value
def __getattr__(self, attr):
#logger.info(" --> ADDING missing installer.%s" % attr)
self.logger.warn(" --> ADDING missing installer.%s" % attr)
setattr(self, attr, None)
return getattr(self, attr)
#def __setattr__(self, attr, value):
# logger.debug(" --> Setting installer.%s to %s" % (attr, repr(value)))
# return super(installer_obj, self).__setattr__(attr, value)
def knobs(self):
for name in self.__dict__:
yield self, name
# Initialize installer settings
installer = installer_obj()
# Create options
options = installer
options.interactive = False
if NUM_VERSION >= 40400:
# IPA version >= 4.4
import sys
import inspect
import gssapi
import logging
import six
from ipapython import version
try:
from ipaclient.install import ipadiscovery
except ImportError:
@@ -63,6 +95,9 @@ if NUM_VERSION >= 40400:
from ipalib import certstore
from ipalib.rpc import delete_persistent_client_session_data
from ipapython import certdb, ipautil
from ipapython.admintool import ScriptError
from ipapython.ipautil import CheckedIPAddress
from ipalib.util import validate_domain_name, normalize_hostname
from ipaplatform import services
from ipaplatform.paths import paths
from ipaplatform.tasks import tasks
@@ -84,7 +119,11 @@ if NUM_VERSION >= 40400:
configure_certmonger, update_ssh_keys, configure_openldap_conf, \
hardcode_ldap_server, get_certs_from_ldap, save_state, \
create_ipa_nssdb, configure_ssh_config, configure_sshd_config, \
configure_automount, configure_firefox, configure_nisdomain
configure_automount, configure_firefox, configure_nisdomain, \
CLIENT_INSTALL_ERROR, is_ipa_client_installed, \
CLIENT_ALREADY_CONFIGURED, nssldap_exists, remove_file, \
check_ip_addresses, print_port_conf_info, configure_ipa_conf, \
purge_host_keytab, configure_sssd_conf
except ImportError:
# Create temporary copy of ipa-client-install script (as
# ipa_client_install.py) to be able to import the script easily
@@ -125,6 +164,7 @@ if NUM_VERSION >= 40400:
configure_krb5_conf = ipa_client_install.configure_krb5_conf
if NUM_VERSION < 40100:
get_ca_cert = ipa_client_install.get_ca_cert
get_ca_certs = None
else:
get_ca_certs = ipa_client_install.get_ca_certs
SECURE_PATH = ("/bin:/sbin:/usr/kerberos/bin:/usr/kerberos/sbin:/usr/bin:/usr/sbin")
@@ -179,9 +219,16 @@ if NUM_VERSION >= 40400:
except ImportError:
check_ldap_conf = None
try:
from ipaclient.install.client import sssd_enable_ifp
except ImportError:
sssd_enable_ifp = None
logger = logging.getLogger("ipa-client-install")
root_logger = logger
else:
# IPA version < 4.4
raise Exception("freeipa version '%s' is too old" % VERSION)

View File

@@ -21,17 +21,36 @@
ipaclient_servers: "{{ groups['ipaserver'] | list }}"
when: ipaclient_no_dns_lookup | bool and groups.ipaserver is defined and ipaclient_servers is not defined
- name: Install - IPA discovery
- fail: msg="ipaadmin_principal and ipaadmin_keytab cannot be used together"
when: ipaadmin_keytab is defined and ipaadmin_principal is defined
- name: Install - Set default principal if no keytab is given
set_fact:
ipaadmin_principal: admin
when: ipaadmin_principal is undefined and ipaclient_keytab is undefined
- name: Install - IPA client test
ipaclient_test:
### basic ###
domain: "{{ ipaserver_domain | default(ipaclient_domain) | default(omit) }}"
servers: "{{ ipaclient_servers | default(omit) }}"
realm: "{{ ipaserver_realm | default(ipaclient_realm) | default(omit) }}"
hostname: "{{ ipaclient_hostname | default(ansible_fqdn) }}"
ca_cert_file: "{{ ipaclient_ca_cert_file | default(omit) }}"
on_master: "{{ ipaclient_on_master }}"
ntp_servers: "{{ ipaclient_ntp_servers | default([]) }}"
ntp_servers: "{{ ipaclient_ntp_servers | default(omit) }}"
ntp_pool: "{{ ipaclient_ntp_pool | default(omit) }}"
no_ntp: "{{ ipaclient_no_ntp }}"
force_ntpd: "{{ ipaclient_force_ntpd }}"
nisdomain: "{{ ipaclient_nisdomain | default(omit) }}"
no_nisdomain: "{{ ipaclient_no_nisdomain }}"
kinit_attempts: "{{ ipaclient_kinit_attempts }}"
ca_cert_files: "{{ ipaclient_ca_cert_file | default(omit) }}"
configure_firefox: "{{ ipaclient_configure_firefox }}"
firefox_dir: "{{ ipaclient_firefox_dir | default(omit) }}"
ip_addresses: "{{ ipaclient_ip_addresses | default(omit) }}"
all_ip_addresses: "{{ ipahost_all_ip_addresses }}"
on_master: "{{ ipaclient_on_master }}"
### sssd ###
enable_dns_updates: "{{ ipassd_enable_dns_updates }}"
register: result_ipaclient_test
- name: Install - Set default principal if no keytab is given