install: Migrate ipa-replica-install to the install framework

https://fedorahosted.org/freeipa/ticket/4468

Reviewed-By: David Kupka <dkupka@redhat.com>
This commit is contained in:
Jan Cholasta 2015-06-09 11:42:50 +00:00
parent 6f1ae05d8d
commit 46cbe26b51
3 changed files with 275 additions and 201 deletions

View File

@ -18,148 +18,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import sys from ipapython.install import cli
import os
from optparse import OptionGroup
from ipapython import ipautil
from ipaserver.install import installutils
from ipaserver.install import server
from ipapython import version
from ipapython.config import IPAOptionParser
from ipapython.ipa_log_manager import root_logger, standard_logging_setup
from ipapython.dn import DN
from ipaplatform.paths import paths from ipaplatform.paths import paths
from ipaserver.install.server import Replica
log_file_name = paths.IPAREPLICA_INSTALL_LOG
DIRMAN_DN = DN(('cn', 'directory manager'))
REPLICA_INFO_TOP_DIR = None
def parse_options(): ReplicaInstall = cli.install_tool(
usage = "%prog [options] REPLICA_FILE" Replica,
parser = IPAOptionParser(usage=usage, version=version.VERSION) command_name='ipa-replica-install',
positional_arguments='replica_file',
basic_group = OptionGroup(parser, "basic options") usage='%prog [options] REPLICA_FILE',
basic_group.add_option("--setup-ca", dest="setup_ca", action="store_true", log_file_name=paths.IPAREPLICA_INSTALL_LOG,
default=False, help="configure a dogtag CA") debug_option=True,
basic_group.add_option("--setup-kra", dest="setup_kra", action="store_true", )
default=False, help="configure a dogtag KRA")
basic_group.add_option("--ip-address", dest="ip_addresses",
type="ip", ip_local=True, action="append", default=[],
help="Replica server IP Address. This option can be used multiple times", metavar="IP_ADDRESS")
basic_group.add_option("-p", "--password", dest="password", sensitive=True,
help="Directory Manager (existing master) password")
basic_group.add_option("-w", "--admin-password", dest="admin_password", sensitive=True,
help="Admin user Kerberos password used for connection check")
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("-N", "--no-ntp", dest="conf_ntp", action="store_false",
help="do not configure ntp", default=True)
basic_group.add_option("--no-ui-redirect", dest="ui_redirect", action="store_false",
default=True, help="Do not automatically redirect to the Web UI")
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("--skip-conncheck", dest="skip_conncheck", action="store_true",
default=False, help="skip connection check to remote master")
basic_group.add_option("-d", "--debug", dest="debug", action="store_true",
default=False, help="gather extra debugging information")
basic_group.add_option("-U", "--unattended", dest="unattended", action="store_true",
default=False, help="unattended installation never prompts the user")
parser.add_option_group(basic_group)
cert_group = OptionGroup(parser, "certificate system options")
cert_group.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
default=True, help="disables pkinit setup steps")
cert_group.add_option("--skip-schema-check", dest="skip_schema_check", action="store_true",
default=False, help="skip check for updated CA DS schema on the remote master")
parser.add_option_group(cert_group)
dns_group = OptionGroup(parser, "DNS options")
dns_group.add_option("--setup-dns", dest="setup_dns", action="store_true",
default=False, help="configure bind with our zone")
dns_group.add_option("--forwarder", dest="forwarders", action="append",
type="ip", help="Add a DNS forwarder. This option can be used multiple times")
dns_group.add_option("--no-forwarders", dest="no_forwarders", action="store_true",
default=False, help="Do not add any DNS forwarders, use root servers instead")
dns_group.add_option("--reverse-zone", dest="reverse_zones", default=[],
action="append", help="The reverse DNS zone to use. This option can be used multiple times",
metavar="REVERSE_ZONE")
dns_group.add_option("--no-reverse", dest="no_reverse", action="store_true",
default=False, help="Do not create new reverse DNS zone")
dns_group.add_option("--no-dnssec-validation", dest="no_dnssec_validation", action="store_true",
default=False, help="Disable DNSSEC validation")
dns_group.add_option("--no-host-dns", dest="no_host_dns", action="store_true",
default=False,
help="Do not use DNS for hostname lookup during installation")
dns_group.add_option("--no-dns-sshfp", dest="create_sshfp", default=True, action="store_false",
help="do not automatically create DNS SSHFP records")
parser.add_option_group(dns_group)
options, args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
if len(args) != 1:
parser.error("you must provide a file generated by ipa-replica-prepare")
if not options.setup_dns:
if options.forwarders:
parser.error("You cannot specify a --forwarder option without the --setup-dns option")
if options.no_forwarders:
parser.error("You cannot specify a --no-forwarders option without the --setup-dns option")
if options.reverse_zones:
parser.error("You cannot specify a --reverse-zone option without the --setup-dns option")
if options.no_reverse:
parser.error("You cannot specify a --no-reverse option without the --setup-dns option")
if options.no_dnssec_validation:
parser.error("You cannot specify a --no-dnssec-validation option without the --setup-dns option")
elif options.forwarders and options.no_forwarders:
parser.error("You cannot specify a --forwarder option together with --no-forwarders")
elif not options.forwarders and not options.no_forwarders:
parser.error("You must specify at least one --forwarder option or --no-forwarders option")
elif options.reverse_zones and options.no_reverse:
parser.error("You cannot specify a --reverse-zone option together with --no-reverse")
options.external_ca = None
options.external_cert_files = None
options.zonemgr = None
options.dnssec_master = False
return safe_options, options, args[0]
def main(): ReplicaInstall.run_cli()
safe_options, options, filename = parse_options()
if os.geteuid() != 0:
sys.exit("\nYou must be root to run this script.\n")
standard_logging_setup(log_file_name, debug=options.debug)
root_logger.debug('%s was invoked with argument "%s" and options: %s' % (sys.argv[0], filename, safe_options))
root_logger.debug('IPA version %s' % version.VENDOR_VERSION)
if not ipautil.file_exists(filename):
sys.exit("Replica file %s does not exist" % filename)
server.replica_install_check(filename, options)
server.replica_install(filename, options)
fail_message = '''
Your system may be partly configured.
Run /usr/sbin/ipa-server-install --uninstall to clean up.
'''
if __name__ == '__main__':
installutils.run_script(main, log_file_name=log_file_name,
operation_name='ipa-replica-install',
fail_message=fail_message)

View File

@ -3,7 +3,6 @@
# #
from .install import Server from .install import Server
from .replicainstall import Replica
from .replicainstall import install_check as replica_install_check
from .replicainstall import install as replica_install
from .upgrade import upgrade_check, upgrade from .upgrade import upgrade_check, upgrade

View File

@ -14,6 +14,9 @@ import tempfile
from ipapython import dogtag, ipautil, sysrestore from ipapython import dogtag, ipautil, sysrestore
from ipapython.dn import DN from ipapython.dn import DN
from ipapython.install import common, core
from ipapython.install.common import step
from ipapython.install.core import Knob
from ipapython.ipa_log_manager import root_logger from ipapython.ipa_log_manager import root_logger
from ipaplatform import services from ipaplatform import services
from ipaplatform.tasks import tasks from ipaplatform.tasks import tasks
@ -28,7 +31,6 @@ from ipaserver.install.replication import (
ReplicationManager, replica_conn_check) ReplicationManager, replica_conn_check)
DIRMAN_DN = DN(('cn', 'directory manager')) DIRMAN_DN = DN(('cn', 'directory manager'))
REPLICA_INFO_TOP_DIR = None
def get_dirman_password(): def get_dirman_password():
@ -162,7 +164,7 @@ def install_dns_records(config, options, remote_api):
config.realm_name, config.realm_name,
config.domain_name, config.domain_name,
reverse_zone, reverse_zone,
options.conf_ntp, not options.no_ntp,
options.setup_ca) options.setup_ca)
except errors.NotFound, e: except errors.NotFound, e:
root_logger.debug('Replica DNS records could not be added ' root_logger.debug('Replica DNS records could not be added '
@ -276,60 +278,38 @@ def check_dns_resolution(host_name, dns_servers):
return no_errors return no_errors
def remove_replica_info_dir(): def remove_replica_info_dir(installer):
# always try to remove decrypted replica file # always try to remove decrypted replica file
try: try:
if REPLICA_INFO_TOP_DIR: if installer._top_dir is not None:
shutil.rmtree(REPLICA_INFO_TOP_DIR) shutil.rmtree(installer._top_dir)
except OSError: except OSError:
pass pass
def init_private_ccache():
global original_ccache
global temp_ccache
(desc, temp_ccache) = tempfile.mkstemp(prefix='krbcc')
os.close(desc)
original_ccache = os.environ.get('KRB5CCNAME')
os.environ['KRB5CCNAME'] = temp_ccache
def destroy_private_ccache():
global original_ccache
global temp_ccache
if original_ccache is not None:
os.environ['KRB5CCNAME'] = original_ccache
else:
os.environ.pop('KRB5CCNAME', None)
if os.path.exists(temp_ccache):
os.remove(temp_ccache)
def common_cleanup(func): def common_cleanup(func):
def decorated(*args, **kwargs): def decorated(installer):
try: try:
try: try:
func(*args, **kwargs) func(installer)
except BaseException: except BaseException:
destroy_private_ccache() remove_replica_info_dir(installer)
remove_replica_info_dir()
raise raise
except KeyboardInterrupt: except KeyboardInterrupt:
sys.exit(1) sys.exit(1)
except Exception:
print(
"Your system may be partly configured.\n"
"Run /usr/sbin/ipa-server-install --uninstall to clean up.\n")
raise
return decorated return decorated
@common_cleanup @common_cleanup
def install_check(filename, options): def install_check(installer):
global config options = installer
filename = installer.replica_file
init_private_ccache()
tasks.check_selinux_status() tasks.check_selinux_status()
@ -339,10 +319,8 @@ def install_check(filename, options):
"Please uninstall it first before configuring the replica, " "Please uninstall it first before configuring the replica, "
"using 'ipa-client-install --uninstall'.") "using 'ipa-client-install --uninstall'.")
global sstore
sstore = sysrestore.StateFile(paths.SYSRESTORE) sstore = sysrestore.StateFile(paths.SYSRESTORE)
global fstore
fstore = sysrestore.FileStore(paths.SYSRESTORE) fstore = sysrestore.FileStore(paths.SYSRESTORE)
# Check to see if httpd is already configured to listen on 443 # Check to see if httpd is already configured to listen on 443
@ -351,7 +329,7 @@ def install_check(filename, options):
check_dirsrv() check_dirsrv()
if options.conf_ntp: if not options.no_ntp:
try: try:
ipaclient.ntpconf.check_timedate_services() ipaclient.ntpconf.check_timedate_services()
except ipaclient.ntpconf.NTPConflictingService, e: except ipaclient.ntpconf.NTPConflictingService, e:
@ -373,8 +351,7 @@ def install_check(filename, options):
sys.exit("Directory Manager password required") sys.exit("Directory Manager password required")
config = create_replica_config(dirman_password, filename, options) config = create_replica_config(dirman_password, filename, options)
global REPLICA_INFO_TOP_DIR installer._top_dir = config.top_dir
REPLICA_INFO_TOP_DIR = config.top_dir
config.setup_ca = options.setup_ca config.setup_ca = options.setup_ca
config.setup_kra = options.setup_kra config.setup_kra = options.setup_kra
@ -398,7 +375,7 @@ def install_check(filename, options):
dns.install_check(False, True, options, config.host_name) dns.install_check(False, True, options, config.host_name)
else: else:
installutils.get_server_ip_address(config.host_name, fstore, installutils.get_server_ip_address(config.host_name, fstore,
options.unattended, False, not installer.interactive, False,
options.ip_addresses) options.ip_addresses)
# check connection # check connection
@ -407,18 +384,22 @@ def install_check(filename, options):
config.master_host_name, config.host_name, config.realm_name, config.master_host_name, config.host_name, config.realm_name,
options.setup_ca, config.ca_ds_port, options.admin_password) options.setup_ca, config.ca_ds_port, options.admin_password)
# Automatically disable pkinit w/ dogtag until that is supported
options.setup_pkinit = False
cafile = config.dir + "/ca.crt" cafile = config.dir + "/ca.crt"
if not ipautil.file_exists(cafile): if not ipautil.file_exists(cafile):
raise RuntimeError("CA cert file is not available. Please run " raise RuntimeError("CA cert file is not available. Please run "
"ipa-replica-prepare to create a new replica file.") "ipa-replica-prepare to create a new replica file.")
installer._fstore = fstore
installer._sstore = sstore
installer._config = config
@common_cleanup @common_cleanup
def install(filename, options): def install(installer):
global config options = installer
fstore = installer._fstore
sstore = installer._sstore
config = installer._config
dogtag_constants = dogtag.install_constants dogtag_constants = dogtag.install_constants
@ -544,7 +525,7 @@ def install(filename, options):
resolution_ok = ( resolution_ok = (
check_dns_resolution(master, dns_masters) and check_dns_resolution(master, dns_masters) and
check_dns_resolution(config.host_name, dns_masters)) check_dns_resolution(config.host_name, dns_masters))
if not resolution_ok and not options.unattended: if not resolution_ok and installer.interactive:
if not ipautil.user_input("Continue?", False): if not ipautil.user_input("Continue?", False):
sys.exit(0) sys.exit(0)
else: else:
@ -562,7 +543,7 @@ def install(filename, options):
replman.conn.unbind() replman.conn.unbind()
# Configure ntpd # Configure ntpd
if options.conf_ntp: if not options.no_ntp:
ipaclient.ntpconf.force_ntpd(sstore) ipaclient.ntpconf.force_ntpd(sstore)
ntp = ntpinstance.NTPInstance() ntp = ntpinstance.NTPInstance()
ntp.create_instance() ntp.create_instance()
@ -584,8 +565,8 @@ def install(filename, options):
ca.install(False, config, options) ca.install(False, config, options)
krb = install_krb(config, setup_pkinit=options.setup_pkinit) krb = install_krb(config, setup_pkinit=not options.no_pkinit)
http = install_http(config, auto_redirect=options.ui_redirect) http = install_http(config, auto_redirect=not options.no_ui_redirect)
otpd = otpdinstance.OtpdInstance() otpd = otpdinstance.OtpdInstance()
otpd.create_instance('OTPD', config.host_name, config.dirman_password, otpd.create_instance('OTPD', config.host_name, config.dirman_password,
@ -625,13 +606,13 @@ def install(filename, options):
args = [paths.IPA_CLIENT_INSTALL, "--on-master", "--unattended", args = [paths.IPA_CLIENT_INSTALL, "--on-master", "--unattended",
"--domain", config.domain_name, "--server", config.host_name, "--domain", config.domain_name, "--server", config.host_name,
"--realm", config.realm_name] "--realm", config.realm_name]
if not options.create_sshfp: if options.no_dns_sshfp:
args.append("--no-dns-sshfp") args.append("--no-dns-sshfp")
if options.trust_sshfp: if options.ssh_trust_dns:
args.append("--ssh-trust-dns") args.append("--ssh-trust-dns")
if not options.conf_ssh: if options.no_ssh:
args.append("--no-ssh") args.append("--no-ssh")
if not options.conf_sshd: if options.no_sshd:
args.append("--no-sshd") args.append("--no-sshd")
if options.mkhomedir: if options.mkhomedir:
args.append("--mkhomedir") args.append("--mkhomedir")
@ -646,5 +627,228 @@ def install(filename, options):
# Everything installed properly, activate ipa service. # Everything installed properly, activate ipa service.
services.knownservices.ipa.enable() services.knownservices.ipa.enable()
destroy_private_ccache() remove_replica_info_dir(installer)
remove_replica_info_dir()
class ReplicaCA(common.Installable, core.Group, core.Composite):
description = "certificate system"
no_pkinit = Knob(
bool, False,
description="disables pkinit setup steps",
)
skip_schema_check = Knob(
bool, False,
description="skip check for updated CA DS schema on the remote master",
)
class ReplicaDNS(common.Installable, core.Group, core.Composite):
description = "DNS"
setup_dns = Knob(
bool, False,
description="configure bind with our zone",
)
forwarders = Knob(
(list, 'ip'), None,
description=("Add a DNS forwarder. This option can be used multiple "
"times"),
cli_name='forwarder',
)
no_forwarders = Knob(
bool, False,
description="Do not add any DNS forwarders, use root servers instead",
)
reverse_zones = Knob(
(list, str), [],
description=("The reverse DNS zone to use. This option can be used "
"multiple times"),
cli_name='reverse-zone',
)
no_reverse = Knob(
bool, False,
description="Do not create new reverse DNS zone",
)
no_dnssec_validation = Knob(
bool, False,
description="Disable DNSSEC validation",
)
no_host_dns = Knob(
bool, False,
description="Do not use DNS for hostname lookup during installation",
)
no_dns_sshfp = Knob(
bool, False,
description="do not automatically create DNS SSHFP records",
)
class Replica(common.Installable, common.Interactive, core.Composite):
replica_file = Knob(
str, None,
description="a file generated by ipa-replica-prepare",
)
setup_ca = Knob(
bool, False,
initializable=False,
description="configure a dogtag CA",
)
setup_kra = Knob(
bool, False,
initializable=False,
description="configure a dogtag KRA",
)
ip_addresses = Knob(
(list, 'ip-local'), None,
description=("Replica server IP Address. This option can be used "
"multiple times"),
cli_name='ip-address',
)
password = Knob(
str, None,
sensitive=True,
description="Directory Manager (existing master) password",
cli_short_name='p',
)
master_password = Knob(
str, None,
sensitive=True,
deprecated=True,
description="kerberos master password (normally autogenerated)",
cli_short_name='P',
)
admin_password = Knob(
str, None,
sensitive=True,
description="Admin user Kerberos password used for connection check",
cli_short_name='w',
)
mkhomedir = Knob(
bool, False,
description="create home directories for users on their first login",
)
no_ntp = Knob(
bool, False,
description="do not configure ntp",
)
no_ui_redirect = Knob(
bool, False,
description="Do not automatically redirect to the Web UI",
)
ssh_trust_dns = Knob(
bool, False,
description="configure OpenSSH client to trust DNS SSHFP records",
)
no_ssh = Knob(
bool, False,
description="do not configure OpenSSH client",
)
no_sshd = Knob(
bool, False,
description="do not configure OpenSSH server",
)
skip_conncheck = Knob(
bool, False,
description="skip connection check to remote master",
)
def __init__(self, **kwargs):
super(Replica, self).__init__(**kwargs)
self._top_dir = None
self._config = None
#pylint: disable=no-member
if self.replica_file is None:
raise RuntimeError(
"you must provide a file generated by ipa-replica-prepare")
if not ipautil.file_exists(self.replica_file):
raise RuntimeError(
"Replica file %s does not exist" % self.replica_file)
if not self.dns.setup_dns:
if self.dns.forwarders:
raise RuntimeError(
"You cannot specify a --forwarder option without the "
"--setup-dns option")
if self.dns.no_forwarders:
raise RuntimeError(
"You cannot specify a --no-forwarders option without the "
"--setup-dns option")
if self.dns.reverse_zones:
raise RuntimeError(
"You cannot specify a --reverse-zone option without the "
"--setup-dns option")
if self.dns.no_reverse:
raise RuntimeError(
"You cannot specify a --no-reverse option without the "
"--setup-dns option")
if self.dns.no_dnssec_validation:
raise RuntimeError(
"You cannot specify a --no-dnssec-validation option "
"without the --setup-dns option")
elif self.dns.forwarders and self.dns.no_forwarders:
raise RuntimeError(
"You cannot specify a --forwarder option together with "
"--no-forwarders")
elif not self.dns.forwarders and not self.dns.no_forwarders:
raise RuntimeError(
"You must specify at least one --forwarder option or "
"--no-forwarders option")
elif self.dns.reverse_zones and self.dns.no_reverse:
raise RuntimeError(
"You cannot specify a --reverse-zone option together with "
"--no-reverse")
# Automatically disable pkinit w/ dogtag until that is supported
self.ca.no_pkinit = True
self.external_ca = False
self.external_cert_files = None
self.no_pkinit = self.ca.no_pkinit
self.skip_schema_check = self.ca.skip_schema_check
self.setup_dns = self.dns.setup_dns
self.forwarders = self.dns.forwarders
self.no_forwarders = self.dns.no_forwarders
self.reverse_zones = self.dns.reverse_zones
self.no_reverse = self.dns.no_reverse
self.no_dnssec_validation = self.dns.no_dnssec_validation
self.dnssec_master = False
self.zonemgr = None
self.no_host_dns = self.dns.no_host_dns
self.no_dns_sshfp = self.dns.no_dns_sshfp
self.unattended = not self.interactive
@step()
def main(self):
install_check(self)
yield
install(self)
ca = core.Component(ReplicaCA)
dns = core.Component(ReplicaDNS)