mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
install: migrate client install to the new class hierarchy
Migrate ipa-client-install from the custom script to the new installer class hierarchy classes. https://fedorahosted.org/freeipa/ticket/6392 Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
parent
714699a81f
commit
09423acb65
@ -19,232 +19,6 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
from ipaclient.install import ipa_client_install
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from optparse import SUPPRESS_HELP, OptionGroup, OptionValueError
|
||||
|
||||
from ipaclient.install import client
|
||||
from ipapython.ipa_log_manager import standard_logging_setup, root_logger
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython import version
|
||||
from ipapython.admintool import ScriptError
|
||||
from ipapython.config import IPAOptionParser
|
||||
from ipalib import x509
|
||||
from ipalib.util import normalize_hostname, validate_domain_name
|
||||
|
||||
|
||||
def parse_options():
|
||||
def validate_ca_cert_file_option(option, opt, value, parser):
|
||||
if not os.path.exists(value):
|
||||
raise OptionValueError("%s option '%s' does not exist" % (opt, value))
|
||||
if not os.path.isfile(value):
|
||||
raise OptionValueError("%s option '%s' is not a file" % (opt, value))
|
||||
if not os.path.isabs(value):
|
||||
raise OptionValueError("%s option '%s' is not an absolute file path" % (opt, value))
|
||||
|
||||
try:
|
||||
x509.load_certificate_from_file(value)
|
||||
except Exception:
|
||||
raise OptionValueError("%s option '%s' is not a valid certificate file" % (opt, value))
|
||||
|
||||
parser.values.ca_cert_file = value
|
||||
|
||||
def kinit_attempts_callback(option, opt, value, parser):
|
||||
if value < 1:
|
||||
raise OptionValueError(
|
||||
"Option %s expects an integer greater than 0."
|
||||
% opt)
|
||||
|
||||
parser.values.kinit_attempts = value
|
||||
|
||||
parser = IPAOptionParser(version=version.VERSION)
|
||||
|
||||
basic_group = OptionGroup(parser, "basic options")
|
||||
basic_group.add_option("--domain", dest="domain", help="domain name")
|
||||
basic_group.add_option("--server", dest="server", help="FQDN of IPA server", action="append")
|
||||
basic_group.add_option("--realm", dest="realm_name", help="realm name")
|
||||
basic_group.add_option("--fixed-primary", dest="primary", action="store_true",
|
||||
default=False, help="Configure sssd to use fixed server as primary IPA server")
|
||||
basic_group.add_option("-p", "--principal", dest="principal",
|
||||
help="principal to use to join the IPA realm")
|
||||
basic_group.add_option("-w", "--password", dest="password", sensitive=True,
|
||||
help="password to join the IPA realm (assumes bulk "
|
||||
"password unless principal is also set)")
|
||||
basic_group.add_option("-k", "--keytab", dest="keytab",
|
||||
help="path to backed up keytab from previous enrollment")
|
||||
basic_group.add_option("-W", dest="prompt_password", action="store_true",
|
||||
default=False,
|
||||
help="Prompt for a password to join the IPA realm")
|
||||
basic_group.add_option("--mkhomedir", dest="mkhomedir",
|
||||
action="store_true", default=False,
|
||||
help="create home directories for users on their first login")
|
||||
basic_group.add_option("", "--hostname", dest="hostname",
|
||||
help="The hostname of this machine (FQDN). If specified, the hostname will be set and "
|
||||
"the system configuration will be updated to persist over reboot. "
|
||||
"By default the result of getfqdn() call from "
|
||||
"Python's socket module is used.")
|
||||
basic_group.add_option("", "--force-join", dest="force_join",
|
||||
action="store_true", default=False,
|
||||
help="Force client enrollment even if already enrolled")
|
||||
basic_group.add_option("--ntp-server", dest="ntp_servers", action="append",
|
||||
help="ntp server to use. This option can be used "
|
||||
"multiple times")
|
||||
basic_group.add_option("-N", "--no-ntp", action="store_false",
|
||||
help="do not configure ntp", default=True, dest="conf_ntp")
|
||||
basic_group.add_option("", "--force-ntpd", dest="force_ntpd",
|
||||
action="store_true", default=False,
|
||||
help="Stop and disable any time&date synchronization services besides ntpd")
|
||||
basic_group.add_option("--nisdomain", dest="nisdomain",
|
||||
help="NIS domain name")
|
||||
basic_group.add_option("--no-nisdomain", action="store_true", default=False,
|
||||
help="do not configure NIS domain name",
|
||||
dest="no_nisdomain")
|
||||
basic_group.add_option("--ssh-trust-dns", dest="trust_sshfp", default=False, action="store_true",
|
||||
help="configure OpenSSH client to trust DNS SSHFP records")
|
||||
basic_group.add_option("--no-ssh", dest="conf_ssh", default=True, action="store_false",
|
||||
help="do not configure OpenSSH client")
|
||||
basic_group.add_option("--no-sshd", dest="conf_sshd", default=True, action="store_false",
|
||||
help="do not configure OpenSSH server")
|
||||
basic_group.add_option("--no-sudo", dest="conf_sudo", default=True,
|
||||
action="store_false",
|
||||
help="do not configure SSSD as data source for sudo")
|
||||
basic_group.add_option("--no-dns-sshfp", dest="create_sshfp", default=True, action="store_false",
|
||||
help="do not automatically create DNS SSHFP records")
|
||||
basic_group.add_option("--noac", dest="no_ac", default=False, action="store_true",
|
||||
help="do not modify the nsswitch.conf and PAM configuration")
|
||||
basic_group.add_option("-f", "--force", dest="force", action="store_true",
|
||||
default=False, help="force setting of LDAP/Kerberos conf")
|
||||
basic_group.add_option('--kinit-attempts', dest='kinit_attempts',
|
||||
action='callback', type='int', default=5,
|
||||
callback=kinit_attempts_callback,
|
||||
help=("number of attempts to obtain host TGT"
|
||||
" (defaults to %default)."))
|
||||
basic_group.add_option("-d", "--debug", dest="debug", action="store_true",
|
||||
default=False, help="print debugging information")
|
||||
basic_group.add_option("-U", "--unattended", dest="unattended",
|
||||
action="store_true",
|
||||
help="unattended (un)installation never prompts the user")
|
||||
basic_group.add_option("--ca-cert-file", dest="ca_cert_file",
|
||||
type="string", action="callback", callback=validate_ca_cert_file_option,
|
||||
help="load the CA certificate from this file")
|
||||
basic_group.add_option("--request-cert", dest="request_cert",
|
||||
action="store_true", default=False,
|
||||
help="request certificate for the machine")
|
||||
# --on-master is used in ipa-server-install and ipa-replica-install
|
||||
# only, it isn't meant to be used on clients.
|
||||
basic_group.add_option("--on-master", dest="on_master", action="store_true",
|
||||
help=SUPPRESS_HELP, default=False)
|
||||
basic_group.add_option("--automount-location", dest="location",
|
||||
help="Automount location")
|
||||
basic_group.add_option("--configure-firefox", dest="configure_firefox",
|
||||
action="store_true", default=False,
|
||||
help="configure Firefox to use IPA domain credentials")
|
||||
basic_group.add_option("--firefox-dir", dest="firefox_dir", default=None,
|
||||
help="specify directory where Firefox is installed (for example: '/usr/lib/firefox')")
|
||||
basic_group.add_option("--ip-address", dest="ip_addresses", default=[],
|
||||
action="append", help="Specify IP address that should be added to DNS."
|
||||
" This option can be used multiple times")
|
||||
basic_group.add_option("--all-ip-addresses", dest="all_ip_addresses",
|
||||
default=False, action="store_true", help="All routable IP"
|
||||
" addresses configured on any inteface will be added to DNS")
|
||||
parser.add_option_group(basic_group)
|
||||
|
||||
sssd_group = OptionGroup(parser, "SSSD options")
|
||||
sssd_group.add_option("--permit", dest="permit",
|
||||
action="store_true", default=False,
|
||||
help="disable access rules by default, permit all access.")
|
||||
sssd_group.add_option("", "--enable-dns-updates", dest="dns_updates",
|
||||
action="store_true", default=False,
|
||||
help="Configures the machine to attempt dns updates when the ip address changes.")
|
||||
sssd_group.add_option("--no-krb5-offline-passwords", dest="krb5_offline_passwords",
|
||||
action="store_false", default=True,
|
||||
help="Configure SSSD not to store user password when the server is offline")
|
||||
sssd_group.add_option("-S", "--no-sssd", dest="sssd",
|
||||
action="store_false", default=True,
|
||||
help="Do not configure the client to use SSSD for authentication")
|
||||
sssd_group.add_option("--preserve-sssd", dest="preserve_sssd",
|
||||
action="store_true", default=False,
|
||||
help="Preserve old SSSD configuration if possible")
|
||||
parser.add_option_group(sssd_group)
|
||||
|
||||
uninstall_group = OptionGroup(parser, "uninstall options")
|
||||
uninstall_group.add_option("", "--uninstall", dest="uninstall", action="store_true",
|
||||
default=False, help="uninstall an existing installation. The uninstall can " \
|
||||
"be run with --unattended option")
|
||||
parser.add_option_group(uninstall_group)
|
||||
|
||||
options, _args = parser.parse_args()
|
||||
safe_opts = parser.get_safe_opts(options)
|
||||
|
||||
if (options.server and not options.domain):
|
||||
parser.error("--server cannot be used without providing --domain")
|
||||
|
||||
if options.domain:
|
||||
try:
|
||||
validate_domain_name(options.domain)
|
||||
except ValueError as ex:
|
||||
parser.error("invalid domain name: %s" % ex)
|
||||
options.domain = normalize_hostname(options.domain)
|
||||
|
||||
if options.force_ntpd and not options.conf_ntp:
|
||||
parser.error("--force-ntpd cannot be used together with --no-ntp")
|
||||
|
||||
if options.firefox_dir and not options.configure_firefox:
|
||||
parser.error("--firefox-dir cannot be used without --configure-firefox option")
|
||||
|
||||
if options.no_nisdomain and options.nisdomain:
|
||||
parser.error("--no-nisdomain cannot be used together with --nisdomain")
|
||||
|
||||
if options.ip_addresses:
|
||||
if options.dns_updates:
|
||||
parser.error("--ip-address cannot be used together with"
|
||||
" --enable-dns-updates")
|
||||
|
||||
if options.all_ip_addresses:
|
||||
parser.error("--ip-address cannot be used together with"
|
||||
" --all-ip-addresses")
|
||||
|
||||
return safe_opts, options
|
||||
|
||||
|
||||
def logging_setup(options):
|
||||
log_file = paths.IPACLIENT_INSTALL_LOG
|
||||
|
||||
if options.uninstall:
|
||||
log_file = paths.IPACLIENT_UNINSTALL_LOG
|
||||
|
||||
standard_logging_setup(
|
||||
filename=log_file, verbose=True, debug=options.debug,
|
||||
console_format='%(message)s')
|
||||
|
||||
|
||||
def main():
|
||||
safe_options, options = parse_options()
|
||||
|
||||
logging_setup(options)
|
||||
root_logger.debug(
|
||||
'%s was invoked with options: %s', sys.argv[0], safe_options)
|
||||
root_logger.debug("missing options might be asked for interactively later")
|
||||
root_logger.debug('IPA version %s' % version.VENDOR_VERSION)
|
||||
|
||||
if options.uninstall:
|
||||
client.uninstall_check(options)
|
||||
client.uninstall(options)
|
||||
else:
|
||||
client.install_check(options)
|
||||
client.install(options)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except ScriptError as e:
|
||||
if e.rval != client.SUCCESS and e.msg:
|
||||
root_logger.error(e.msg)
|
||||
sys.exit(e.rval)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
except RuntimeError as e:
|
||||
sys.exit(e)
|
||||
ipa_client_install.run()
|
||||
|
@ -50,6 +50,7 @@ from ipalib.rpc import delete_persistent_client_session_data
|
||||
from ipalib.util import (
|
||||
broadcast_ip_address_warning,
|
||||
network_ip_address_warning,
|
||||
normalize_hostname,
|
||||
verify_host_resolvable,
|
||||
)
|
||||
from ipaplatform import services
|
||||
@ -67,7 +68,8 @@ from ipapython.admintool import ScriptError
|
||||
from ipapython.dn import DN
|
||||
from ipapython.install import typing
|
||||
from ipapython.install.core import knob
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
from ipapython.install.common import step
|
||||
from ipapython.ipa_log_manager import log_mgr, root_logger
|
||||
from ipapython.ipautil import (
|
||||
CalledProcessError,
|
||||
dir_exists,
|
||||
@ -3309,6 +3311,41 @@ def uninstall(options):
|
||||
raise ScriptError(rval=rv)
|
||||
|
||||
|
||||
def init(installer):
|
||||
try:
|
||||
installer.debug = log_mgr.get_handler('console').level == 'debug'
|
||||
except KeyError:
|
||||
installer.debug = True
|
||||
installer.unattended = not installer.interactive
|
||||
|
||||
if installer.domain_name:
|
||||
installer.domain = normalize_hostname(installer.domain_name)
|
||||
else:
|
||||
installer.domain = None
|
||||
installer.server = installer.servers
|
||||
installer.realm = installer.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
|
||||
installer.conf_ntp = not installer.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
|
||||
|
||||
|
||||
class ClientInstallInterface(hostname_.HostNameInstallInterface,
|
||||
service.ServiceAdminInstallInterface):
|
||||
"""
|
||||
@ -3492,3 +3529,91 @@ class ClientInstallInterface(hostname_.HostNameInstallInterface,
|
||||
raise RuntimeError(
|
||||
"--ip-address cannot be used together with"
|
||||
"--all-ip-addresses")
|
||||
|
||||
|
||||
class ClientInstall(ClientInstallInterface,
|
||||
automount.AutomountInstallInterface):
|
||||
"""
|
||||
Client installer
|
||||
"""
|
||||
|
||||
ca_cert_files = knob(
|
||||
bases=ClientInstallInterface.ca_cert_files,
|
||||
)
|
||||
|
||||
@ca_cert_files.validator
|
||||
def ca_cert_files(self, value):
|
||||
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)
|
||||
|
||||
@property
|
||||
def prompt_password(self):
|
||||
return self.interactive
|
||||
|
||||
automount_location = knob(
|
||||
bases=automount.AutomountInstallInterface.automount_location,
|
||||
default=None,
|
||||
)
|
||||
|
||||
no_ac = knob(
|
||||
None,
|
||||
description="do not modify the nsswitch.conf and PAM configuration",
|
||||
cli_names='--noac',
|
||||
)
|
||||
|
||||
force = knob(
|
||||
None,
|
||||
description="force setting of LDAP/Kerberos conf",
|
||||
cli_names=[None, '-f'],
|
||||
)
|
||||
|
||||
on_master = False
|
||||
|
||||
configure_firefox = knob(
|
||||
None,
|
||||
description="configure Firefox to use IPA domain credentials",
|
||||
)
|
||||
|
||||
firefox_dir = knob(
|
||||
str, None,
|
||||
description="specify directory where Firefox is installed (for "
|
||||
"example: '/usr/lib/firefox')",
|
||||
)
|
||||
|
||||
no_sssd = knob(
|
||||
None,
|
||||
description="Do not configure the client to use SSSD for "
|
||||
"authentication",
|
||||
cli_names=[None, '-S'],
|
||||
)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ClientInstall, self).__init__(**kwargs)
|
||||
|
||||
if self.firefox_dir and not self.configure_firefox:
|
||||
raise RuntimeError(
|
||||
"--firefox-dir cannot be used without --configure-firefox "
|
||||
"option")
|
||||
|
||||
@step()
|
||||
def main(self):
|
||||
init(self)
|
||||
install_check(self)
|
||||
yield
|
||||
install(self)
|
||||
|
||||
@main.uninstaller
|
||||
def main(self):
|
||||
init(self)
|
||||
uninstall_check(self)
|
||||
yield
|
||||
uninstall(self)
|
||||
|
66
ipaclient/install/ipa_client_install.py
Normal file
66
ipaclient/install/ipa_client_install.py
Normal file
@ -0,0 +1,66 @@
|
||||
#
|
||||
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from ipaclient.install import client
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.install import cli
|
||||
from ipapython.install.core import knob
|
||||
|
||||
|
||||
class StandaloneClientInstall(client.ClientInstall):
|
||||
no_host_dns = False
|
||||
no_wait_for_dns = False
|
||||
|
||||
principal = knob(
|
||||
bases=client.ClientInstall.principal,
|
||||
cli_names=list(client.ClientInstall.principal.cli_names) + ['-p'],
|
||||
)
|
||||
|
||||
password = knob(
|
||||
str, None,
|
||||
sensitive=True,
|
||||
description="password to join the IPA realm (assumes bulk password "
|
||||
"unless principal is also set)",
|
||||
cli_names=[None, '-w'],
|
||||
)
|
||||
|
||||
@property
|
||||
def admin_password(self):
|
||||
if self.principal:
|
||||
return self.password
|
||||
|
||||
return super(StandaloneClientInstall, self).admin_password
|
||||
|
||||
@property
|
||||
def host_password(self):
|
||||
if not self.principal:
|
||||
return self.password
|
||||
|
||||
return super(StandaloneClientInstall, self).host_password
|
||||
|
||||
prompt_password = knob(
|
||||
None,
|
||||
description="Prompt for a password to join the IPA realm",
|
||||
cli_names='-W',
|
||||
)
|
||||
|
||||
on_master = knob(
|
||||
None,
|
||||
deprecated=True,
|
||||
)
|
||||
|
||||
|
||||
ClientInstall = cli.install_tool(
|
||||
StandaloneClientInstall,
|
||||
command_name='ipa-client-install',
|
||||
log_file_name=paths.IPACLIENT_INSTALL_LOG,
|
||||
debug_option=True,
|
||||
verbose=True,
|
||||
console_format='%(message)s',
|
||||
uninstall_log_file_name=paths.IPACLIENT_UNINSTALL_LOG,
|
||||
)
|
||||
|
||||
|
||||
def run():
|
||||
ClientInstall.run_cli()
|
Loading…
Reference in New Issue
Block a user