From 225fae841882832668c0842479ab11c89dfcd1a5 Mon Sep 17 00:00:00 2001 From: Jan Cholasta Date: Nov 11 2016 11:17:25 +0000 Subject: install: migrate server installers to the new class hierarchy Migrate ipa-server-install and ipa-replica-install from the old installer classes to the new installer class hierarchy classes. https://fedorahosted.org/freeipa/ticket/6392 Reviewed-By: Martin Basti --- diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install index 27be5f8..44a9aa3 100755 --- a/install/tools/ipa-replica-install +++ b/install/tools/ipa-replica-install @@ -18,17 +18,6 @@ # along with this program. If not, see . # -from ipapython.install import cli -from ipaplatform.paths import paths -from ipaserver.install.server import Replica +from ipaserver.install import ipa_replica_install - -ReplicaInstall = cli.install_tool( - Replica, - command_name='ipa-replica-install', - log_file_name=paths.IPAREPLICA_INSTALL_LOG, - debug_option=True, -) - - -ReplicaInstall.run_cli() +ipa_replica_install.run() diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install index 9fc78b5..17be2f4 100755 --- a/install/tools/ipa-server-install +++ b/install/tools/ipa-server-install @@ -20,18 +20,6 @@ # along with this program. If not, see . # -from ipapython.install import cli -from ipaplatform.paths import paths -from ipaserver.install.server import Server +from ipaserver.install import ipa_server_install - -ServerInstall = cli.install_tool( - Server, - command_name='ipa-server-install', - log_file_name=paths.IPASERVER_INSTALL_LOG, - debug_option=True, - uninstall_log_file_name=paths.IPASERVER_UNINSTALL_LOG, -) - - -ServerInstall.run_cli() +ipa_server_install.run() diff --git a/ipaserver/install/ipa_replica_install.py b/ipaserver/install/ipa_replica_install.py new file mode 100644 index 0000000..39c7456 --- /dev/null +++ b/ipaserver/install/ipa_replica_install.py @@ -0,0 +1,95 @@ +# +# Copyright (C) 2015 FreeIPA Contributors see COPYING for license +# + +from ipapython.install import cli +from ipapython.install.core import knob +from ipaplatform.paths import paths +from ipaserver.install.server import ServerReplicaInstall + + +class CompatServerReplicaInstall(ServerReplicaInstall): + ca_cert_files = None + all_ip_addresses = False + no_wait_for_dns = True + nisdomain = None + no_nisdomain = False + no_sudo = False + request_cert = False + ca_file = None + zonemgr = None + + replica_file = knob( + # pylint: disable=no-member + bases=ServerReplicaInstall.replica_file, + cli_names='replica_file', + ) + + auto_password = knob( + str, None, + description="Password to join the IPA realm. Assumes bulk password " + "unless principal is also set. (domain level 1+) " + "Directory Manager (existing master) password. (domain " + "level 0)", + sensitive=True, + cli_names=['--password', '-p'], + cli_metavar='PASSWORD', + ) + + @property + def dm_password(self): + try: + return self.__dm_password + except AttributeError: + pass + + if self.replica_file is not None: + return self.auto_password + + return super(CompatServerReplicaInstall, self).dm_password + + @dm_password.setter + def dm_password(self, value): + self.__dm_password = value + + ip_addresses = knob( + # pylint: disable=no-member + bases=ServerReplicaInstall.ip_addresses, + description="Replica server IP Address. This option can be used " + "multiple times", + ) + + admin_password = knob( + # pylint: disable=no-member + bases=ServerReplicaInstall.admin_password, + cli_names=list(ServerReplicaInstall.admin_password.cli_names) + ['-w'], + ) + + @admin_password.default_getter + def admin_password(self): + if self.replica_file is None and self.principal: + return self.auto_password + + return super(CompatServerReplicaInstall, self).admin_password + + @property + def host_password(self): + admin_password = ( + super(CompatServerReplicaInstall, self).admin_password) + if (self.replica_file is None and + (not self.principal or admin_password)): + return self.auto_password + + return super(CompatServerReplicaInstall, self).host_password + + +ReplicaInstall = cli.install_tool( + CompatServerReplicaInstall, + command_name='ipa-replica-install', + log_file_name=paths.IPAREPLICA_INSTALL_LOG, + debug_option=True, +) + + +def run(): + ReplicaInstall.run_cli() diff --git a/ipaserver/install/ipa_server_install.py b/ipaserver/install/ipa_server_install.py new file mode 100644 index 0000000..3b6cb81 --- /dev/null +++ b/ipaserver/install/ipa_server_install.py @@ -0,0 +1,49 @@ +# +# Copyright (C) 2015 FreeIPA Contributors see COPYING for license +# + +from ipapython.install import cli +from ipapython.install.core import knob +from ipaplatform.paths import paths +from ipaserver.install.server import ServerMasterInstall + + +class CompatServerMasterInstall(ServerMasterInstall): + all_ip_addresses = False + nisdomain = None + no_nisdomain = False + no_sudo = False + request_cert = False + + new_dm_password = knob( + # pylint: disable=no-member + bases=ServerMasterInstall.new_dm_password, + cli_names=['--ds-password', '-p'], + ) + + new_admin_password = knob( + # pylint: disable=no-member + bases=ServerMasterInstall.new_admin_password, + cli_names=(list(ServerMasterInstall.new_admin_password.cli_names) + + ['-a']), + ) + + ip_addresses = knob( + # pylint: disable=no-member + bases=ServerMasterInstall.ip_addresses, + description="Master Server IP Address. This option can be used " + "multiple times", + ) + + +ServerInstall = cli.install_tool( + CompatServerMasterInstall, + command_name='ipa-server-install', + log_file_name=paths.IPASERVER_INSTALL_LOG, + debug_option=True, + uninstall_log_file_name=paths.IPASERVER_UNINSTALL_LOG, +) + + +def run(): + ServerInstall.run_cli() diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py index a8b56d6..c518ec9 100644 --- a/ipaserver/install/server/__init__.py +++ b/ipaserver/install/server/__init__.py @@ -15,7 +15,10 @@ import random from ipaclient.install import client from ipalib import constants from ipalib.install.service import (enroll_only, + installs_master, + installs_replica, master_install_only, + prepares, prepare_only, replica_install_only) from ipalib.util import validate_domain_name @@ -23,11 +26,17 @@ from ipapython import ipautil from ipapython.dnsutil import check_zone_overlap from ipapython.install import typing from ipapython.install.core import knob +from ipapython.install.common import step from .install import validate_admin_password, validate_dm_password -from .install import Server -from .replicainstall import Replica - +from .install import init as master_init +from .install import install as master_install +from .install import install_check as master_install_check +from .install import uninstall, uninstall_check +from .replicainstall import init as replica_init +from .replicainstall import install as replica_install +from .replicainstall import install_check as replica_install_check +from .replicainstall import promote_check as replica_promote_check from .upgrade import upgrade_check, upgrade from .. import ca, conncheck, dns, kra @@ -538,3 +547,56 @@ class ServerInstallInterface(client.ClientInstallInterface, # Automatically disable pkinit w/ dogtag until that is supported self.no_pkinit = True + + +class ServerMasterInstall(installs_master(ServerInstallInterface)): + """ + Server master installer + """ + + domain_name = None + servers = None + dm_password = None + no_wait_for_dns = True + admin_password = None + host_password = None + keytab = None + setup_ca = True + setup_kra = False + + def __init__(self, **kwargs): + super(ServerMasterInstall, self).__init__(**kwargs) + master_init(self) + + @step() + def main(self): + master_install_check(self) + yield + master_install(self) + + @main.uninstaller + def main(self): + uninstall_check(self) + yield + uninstall(self) + + +class ServerReplicaInstall(installs_replica(ServerInstallInterface)): + """ + Server replica installer + """ + + subject = None + + def __init__(self, **kwargs): + super(ServerReplicaInstall, self).__init__(**kwargs) + replica_init(self) + + @step() + def main(self): + if self.replica_file is None: + replica_promote_check(self) + else: + replica_install_check(self) + yield + replica_install(self) diff --git a/ipaserver/install/server/common.py b/ipaserver/install/server/common.py deleted file mode 100644 index 4ee83f1..0000000 --- a/ipaserver/install/server/common.py +++ /dev/null @@ -1,489 +0,0 @@ -# -# Copyright (C) 2015 FreeIPA Contributors see COPYING for license -# - -from __future__ import print_function - -import os -import sys - -import six - -from ipapython.dn import DN -from ipapython.ipautil import CheckedIPAddress -from ipapython.install import common, core -from ipapython.install.core import Knob, group -from ipalib.util import validate_domain_name -from ipaserver.install import bindinstance -from ipapython.dnsutil import check_zone_overlap - -if six.PY3: - unicode = str - -VALID_SUBJECT_ATTRS = ['st', 'o', 'ou', 'dnqualifier', 'c', - 'serialnumber', 'l', 'title', 'sn', 'givenname', - 'initials', 'generationqualifier', 'dc', 'mail', - 'uid', 'postaladdress', 'postalcode', 'postofficebox', - 'houseidentifier', 'e', 'street', 'pseudonym', - 'incorporationlocality', 'incorporationstate', - 'incorporationcountry', 'businesscategory'] - - -@group -class BaseServerCA(common.Installable, core.Composite): - description = "certificate system" - - external_ca = Knob( - bool, False, - description=("Generate a CSR for the IPA CA certificate to be signed " - "by an external CA"), - ) - - external_ca_type = Knob( - {'generic', 'ms-cs'}, None, - description="Type of the external CA", - ) - - external_cert_files = Knob( - (list, str), None, - description=("File containing the IPA CA certificate and the external " - "CA certificate chain"), - cli_name='external-cert-file', - cli_aliases=['external_cert_file', 'external_ca_file'], - cli_metavar='FILE', - ) - - @external_cert_files.validator - def external_cert_files(self, value): - if any(not os.path.isabs(path) for path in value): - raise ValueError("must use an absolute path") - - dirsrv_cert_files = Knob( - (list, str), None, - description=("File containing the Directory Server SSL certificate " - "and private key"), - cli_name='dirsrv-cert-file', - cli_metavar='FILE', - ) - - http_cert_files = Knob( - (list, str), None, - description=("File containing the Apache Server SSL certificate and " - "private key"), - cli_name='http-cert-file', - cli_metavar='FILE', - ) - - pkinit_cert_files = Knob( - (list, str), None, - description=("File containing the Kerberos KDC SSL certificate and " - "private key"), - cli_name='pkinit-cert-file', - cli_metavar='FILE', - ) - - dirsrv_pin = Knob( - str, None, - sensitive=True, - description="The password to unlock the Directory Server private key", - cli_metavar='PIN', - ) - - http_pin = Knob( - str, None, - sensitive=True, - description="The password to unlock the Apache Server private key", - cli_metavar='PIN', - ) - - pkinit_pin = Knob( - str, None, - sensitive=True, - description="The password to unlock the Kerberos KDC private key", - cli_metavar='PIN', - ) - - dirsrv_cert_name = Knob( - str, None, - description="Name of the Directory Server SSL certificate to install", - cli_metavar='NAME', - ) - - http_cert_name = Knob( - str, None, - description="Name of the Apache Server SSL certificate to install", - cli_metavar='NAME', - ) - - pkinit_cert_name = Knob( - str, None, - description="Name of the Kerberos KDC SSL certificate to install", - cli_metavar='NAME', - ) - - ca_cert_files = Knob( - (list, str), None, - description=("File containing CA certificates for the service " - "certificate files"), - cli_name='ca-cert-file', - cli_aliases=['root-ca-file'], - cli_metavar='FILE', - ) - - subject = Knob( - str, None, - description="The certificate subject base (default O=)", - ) - - @subject.validator - def subject(self, value): - v = unicode(value, 'utf-8') - if any(ord(c) < 0x20 for c in v): - raise ValueError("must not contain control characters") - if '&' in v: - raise ValueError("must not contain an ampersand (\"&\")") - try: - dn = DN(v) - for rdn in dn: - if rdn.attr.lower() not in VALID_SUBJECT_ATTRS: - raise ValueError("invalid attribute: \"%s\"" % rdn.attr) - except ValueError as e: - raise ValueError("invalid subject base format: %s" % e) - - ca_signing_algorithm = Knob( - {'SHA1withRSA', 'SHA256withRSA', 'SHA512withRSA'}, None, - description="Signing algorithm of the IPA CA certificate", - ) - - skip_schema_check = Knob( - bool, False, - description="skip check for updated CA DS schema on the remote master", - ) - - -@group -class BaseServerDNS(common.Installable, core.Composite): - description = "DNS" - - forwarders = Knob( - (list, 'ip'), None, - description=("Add a DNS forwarder. This option can be used multiple " - "times"), - cli_name='forwarder', - ) - - forward_policy = Knob( - {'only', 'first'}, None, - description=("DNS forwarding policy for global forwarders"), - ) - - auto_forwarders = Knob( - bool, False, - description="Use DNS forwarders configured in /etc/resolv.conf", - ) - - no_forwarders = Knob( - bool, False, - description="Do not add any DNS forwarders, use root servers instead", - ) - - allow_zone_overlap = Knob( - bool, False, - description="Create DNS zone even if it already exists", - ) - - reverse_zones = Knob( - (list, str), [], - description=("The reverse DNS zone to use. This option can be used " - "multiple times"), - cli_name='reverse-zone', - cli_metavar='REVERSE_ZONE', - ) - - @reverse_zones.validator - def reverse_zones(self, values): - if not self.allow_zone_overlap: - for zone in values: - check_zone_overlap(zone) - - no_reverse = Knob( - bool, False, - description="Do not create new reverse DNS zone", - ) - - auto_reverse = Knob( - bool, False, - description="Create necessary reverse zones", - ) - - no_dnssec_validation = Knob( - bool, False, - description="Disable DNSSEC validation", - ) - - dnssec_master = Knob( - bool, False, - description="Setup server to be DNSSEC key master", - ) - - disable_dnssec_master = Knob( - bool, False, - description="Disable the DNSSEC master on this server", - ) - - kasp_db_file = Knob( - str, None, - description="Copy OpenDNSSEC metadata from the specified file (will " - "not create a new kasp.db file)", - ) - - force = Knob( - bool, False, - description="Force install", - ) - - zonemgr = Knob( - str, None, - description=("DNS zone manager e-mail address. Defaults to " - "hostmaster@DOMAIN"), - ) - - @zonemgr.validator - def zonemgr(self, value): - # validate the value first - try: - # IDNA support requires unicode - encoding = getattr(sys.stdin, 'encoding', None) - if encoding is None: - encoding = 'utf-8' - value = value.decode(encoding) - bindinstance.validate_zonemgr_str(value) - except ValueError as e: - # FIXME we can do this in better way - # https://fedorahosted.org/freeipa/ticket/4804 - # decode to proper stderr encoding - stderr_encoding = getattr(sys.stderr, 'encoding', None) - if stderr_encoding is None: - stderr_encoding = 'utf-8' - error = unicode(e).encode(stderr_encoding) - raise ValueError(error) - - -class BaseServer(common.Installable, common.Interactive, core.Composite): - realm_name = Knob( - str, None, - description="realm name", - cli_name='realm', - cli_short_name='r', - ) - - domain_name = Knob( - str, None, - description="domain name", - cli_name='domain', - cli_short_name='n', - ) - - @domain_name.validator - def domain_name(self, value): - validate_domain_name(value) - - dm_password = Knob( - str, None, - sensitive=True, - cli_short_name='p', - ) - - admin_password = Knob( - str, None, - sensitive=True, - ) - - mkhomedir = Knob( - bool, False, - description="create home directories for users on their first login", - ) - - host_name = Knob( - str, None, - description="fully qualified name of this host", - cli_name='hostname', - ) - - ip_addresses = Knob( - (list, 'ip'), None, - description=("Master Server IP Address. This option can be used " - "multiple times"), - cli_name='ip-address', - cli_metavar='IP_ADDRESS', - ) - - @ip_addresses.validator - def ip_addresses(self, values): - for value in values: - try: - CheckedIPAddress(value, match_local=True) - except Exception as e: - raise ValueError("invalid IP address {0}: {1}".format( - value, e)) - - no_host_dns = Knob( - bool, False, - description="Do not use DNS for hostname lookup during installation", - ) - - setup_ca = Knob( - bool, False, - description="configure a dogtag CA", - ) - - setup_kra = Knob( - bool, False, - description="configure a dogtag KRA", - ) - - setup_dns = Knob( - bool, False, - description="configure bind with our zone", - ) - - no_ntp = Knob( - bool, False, - description="do not configure ntp", - cli_short_name='N', - ) - - no_pkinit = Knob( - bool, False, - description="disables pkinit setup steps", - ) - - 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", - ) - - no_dns_sshfp = Knob( - bool, False, - description="Do not automatically create DNS SSHFP records", - ) - - dirsrv_config_file = Knob( - str, None, - description="The path to LDIF file that will be used to modify " - "configuration of dse.ldif during installation of the " - "directory server instance", - cli_metavar='FILE', - ) - - @dirsrv_config_file.validator - def dirsrv_config_file(self, value): - if not os.path.exists(value): - raise ValueError("File %s does not exist." % value) - - - def __init__(self, **kwargs): - super(BaseServer, self).__init__(**kwargs) - - #pylint: disable=no-member - - # If any of the key file options are selected, all are required. - cert_file_req = (self.ca.dirsrv_cert_files, self.ca.http_cert_files) - cert_file_opt = (self.ca.pkinit_cert_files,) - if any(cert_file_req + cert_file_opt) and not all(cert_file_req): - raise RuntimeError( - "--dirsrv-cert-file and --http-cert-file are required if any " - "key file options are used.") - - if not self.interactive: - if self.ca.dirsrv_cert_files and self.ca.dirsrv_pin is None: - raise RuntimeError( - "You must specify --dirsrv-pin with --dirsrv-cert-file") - if self.ca.http_cert_files and self.ca.http_pin is None: - raise RuntimeError( - "You must specify --http-pin with --http-cert-file") - if self.ca.pkinit_cert_files and self.ca.pkinit_pin is None: - raise RuntimeError( - "You must specify --pkinit-pin with --pkinit-cert-file") - - if self.ca.external_cert_files and self.ca.dirsrv_cert_files: - raise RuntimeError( - "Service certificate file options cannot be used with the " - "external CA options.") - - if self.ca.external_ca_type and not self.ca.external_ca: - raise RuntimeError( - "You cannot specify --external-ca-type without --external-ca") - - if not self.setup_dns: - if self.dns.forwarders: - raise RuntimeError( - "You cannot specify a --forwarder option without the " - "--setup-dns option") - if self.dns.auto_forwarders: - raise RuntimeError( - "You cannot specify a --auto-forwarders 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.forward_policy: - raise RuntimeError( - "You cannot specify a --forward-policy 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.auto_reverse: - raise RuntimeError( - "You cannot specify a --auto-reverse 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 self.dns.auto_forwarders and self.dns.no_forwarders: - raise RuntimeError( - "You cannot specify a --auto-forwarders option together with " - "--no-forwarders") - elif self.dns.reverse_zones and self.dns.no_reverse: - raise RuntimeError( - "You cannot specify a --reverse-zone option together with " - "--no-reverse") - elif self.dns.auto_reverse and self.dns.no_reverse: - raise RuntimeError( - "You cannot specify a --auto-reverse option together with " - "--no-reverse") - - # Automatically disable pkinit w/ dogtag until that is supported - self.no_pkinit = True - - self.unattended = not self.interactive - - ca = core.Component(BaseServerCA) - dns = core.Component(BaseServerDNS) diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index 22e0cd3..091992a 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -6,7 +6,6 @@ from __future__ import print_function import os import pickle -import random import shutil import sys import tempfile @@ -16,10 +15,6 @@ import six from ipapython import certmonger, ipautil, sysrestore from ipapython.dn import DN -from ipapython.dnsutil import check_zone_overlap -from ipapython.install import core -from ipapython.install.common import step -from ipapython.install.core import Knob from ipapython.ipa_log_manager import root_logger from ipapython.ipautil import ( decrypt_file, format_netloc, ipa_generate_password, run, user_input, @@ -56,7 +51,7 @@ try: except ImportError: _server_trust_ad_installed = False -from .common import BaseServer, BaseServerCA +NoneType = type(None) SYSRESTORE_DIR_PATH = paths.SYSRESTORE @@ -1137,216 +1132,24 @@ def uninstall(installer): sys.exit(rv) -class ServerCA(BaseServerCA): - external_ca = Knob(BaseServerCA.external_ca) - external_ca_type = Knob(BaseServerCA.external_ca_type) - external_cert_files = Knob(BaseServerCA.external_cert_files) +def init(installer): + installer.unattended = not installer.interactive - dirsrv_cert_files = Knob( - BaseServerCA.dirsrv_cert_files, - cli_aliases=['dirsrv_pkcs12'], - ) - - http_cert_files = Knob( - BaseServerCA.http_cert_files, - cli_aliases=['http_pkcs12'], - ) - - pkinit_cert_files = Knob( - BaseServerCA.pkinit_cert_files, - cli_aliases=['pkinit_pkcs12'], - ) - - dirsrv_pin = Knob( - BaseServerCA.dirsrv_pin, - cli_aliases=['dirsrv_pin'], - ) - - http_pin = Knob( - BaseServerCA.http_pin, - cli_aliases=['http_pin'], - ) - - pkinit_pin = Knob( - BaseServerCA.pkinit_pin, - cli_aliases=['pkinit_pin'], - ) - - skip_schema_check = None - - -class Server(BaseServer): - setup_ca = None - setup_kra = None - setup_dns = Knob(BaseServer.setup_dns) - - realm_name = Knob(BaseServer.realm_name) - domain_name = Knob(BaseServer.domain_name) - - @domain_name.validator - def domain_name(self, value): - if (self.setup_dns and - not self.dns.allow_zone_overlap): # pylint: disable=no-member - print("Checking DNS domain %s, please wait ..." % value) - check_zone_overlap(value, False) - - dm_password = Knob( - BaseServer.dm_password, - description="Directory Manager password", - cli_name='ds-password', - ) - - @dm_password.validator - def dm_password(self, value): - validate_dm_password(value) - - master_password = Knob( - str, None, - sensitive=True, - deprecated=True, - description="kerberos master password (normally autogenerated)", - cli_short_name='P', - ) + installer.domain_name = installer.new_domain_name + installer.dm_password = installer.new_dm_password + installer.admin_password = installer.new_admin_password + installer.domainlevel = installer.domain_level - admin_password = Knob( - BaseServer.admin_password, - description="admin user kerberos password", - cli_short_name='a', - ) - - @admin_password.validator - def admin_password(self, value): - validate_admin_password(value) - - mkhomedir = Knob(BaseServer.mkhomedir) - host_name = Knob(BaseServer.host_name) - - domainlevel = Knob( - int, constants.MAX_DOMAIN_LEVEL, - description="IPA domain level", - cli_name='domain-level', - deprecated=True, - ) - - @domainlevel.validator - def domainlevel(self, value): - # Check that Domain Level is within the allowed range - if value < constants.MIN_DOMAIN_LEVEL: - raise ValueError( - "Domain Level cannot be lower than {0}".format( - constants.MIN_DOMAIN_LEVEL)) - elif value > constants.MAX_DOMAIN_LEVEL: - raise ValueError( - "Domain Level cannot be higher than {0}".format( - constants.MAX_DOMAIN_LEVEL)) - - ip_addresses = Knob( - BaseServer.ip_addresses, - description=("Master Server IP Address. This option can be used " - "multiple times"), - ) - - no_host_dns = Knob(BaseServer.no_host_dns) - no_ntp = Knob(BaseServer.no_ntp) - - idstart = Knob( - int, random.randint(1, 10000) * 200000, - description="The starting value for the IDs range (default random)", - ) - - idmax = Knob( - int, - description=("The max value for the IDs range (default: " - "idstart+199999)"), - ) - - @idmax.default_getter - def idmax(self): - return self.idstart + 200000 - 1 - - no_hbac_allow = Knob( - bool, False, - description="Don't install allow_all HBAC rule", - cli_name='no-hbac-allow', - cli_aliases=['no_hbac_allow'], - ) - - ignore_topology_disconnect = Knob( - bool, False, - description="do not check whether server uninstall disconnects the " - "topology (domain level 1+)", - ) - ignore_last_of_role = Knob( - bool, False, - description="do not check whether server uninstall removes last " - "CA/DNS server or DNSSec master (domain level 1+)", - ) - - # dns - dnssec_master = None - disable_dnssec_master = None - kasp_db_file = None - force = None - - def __init__(self, **kwargs): - super(Server, self).__init__(**kwargs) - - self._installation_cleanup = True - self._ds = None - - self._dirsrv_pkcs12_file = None - self._http_pkcs12_file = None - self._pkinit_pkcs12_file = None - self._dirsrv_pkcs12_info = None - self._http_pkcs12_info = None - self._pkinit_pkcs12_info = None - self._external_cert_file = None - self._external_ca_file = None - self._ca_cert = None - self._update_hosts_file = False - - # pylint: disable=no-member - - if self.uninstalling: - if (self.realm_name or self.admin_password or - self.master_password): - raise RuntimeError( - "In uninstall mode, -a, -r and -P options are not allowed") - elif not self.interactive: - if (not self.realm_name or not self.dm_password or - not self.admin_password): - raise RuntimeError( - "In unattended mode you need to provide at least -r, -p " - "and -a options") - if self.setup_dns: - if (not self.dns.forwarders and not self.dns.no_forwarders - and not self.dns.auto_forwarders): - raise RuntimeError( - "You must specify at least one of --forwarder, " - "--auto-forwarders, or --no-forwarders options") - - any_ignore_option_true = any( - [self.ignore_topology_disconnect, self.ignore_last_of_role]) - if any_ignore_option_true and not self.uninstalling: - raise RuntimeError( - "'--ignore-topology-disconnect/--ignore-last-of-role' options " - "can be used only during uninstallation") - - if self.idmax < self.idstart: - raise RuntimeError( - "idmax (%s) cannot be smaller than idstart (%s)" % - (self.idmax, self.idstart)) - - ca = core.Component(ServerCA) - - @step() - def main(self): - install_check(self) - yield - install(self) - - @main.uninstaller - def main(self): - uninstall_check(self) - yield - uninstall(self) + installer._installation_cleanup = True + installer._ds = None + + installer._dirsrv_pkcs12_file = None + installer._http_pkcs12_file = None + installer._pkinit_pkcs12_file = None + installer._dirsrv_pkcs12_info = None + installer._http_pkcs12_info = None + installer._pkinit_pkcs12_info = None + installer._external_cert_file = None + installer._external_ca_file = None + installer._ca_cert = None + installer._update_hosts_file = False diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py index 537f7df..6d618b6 100644 --- a/ipaserver/install/server/replicainstall.py +++ b/ipaserver/install/server/replicainstall.py @@ -4,7 +4,6 @@ from __future__ import print_function -import collections from distutils.version import LooseVersion import dns.exception as dnsexception import dns.name as dnsname @@ -20,8 +19,6 @@ import six from ipapython import ipaldap, ipautil, sysrestore from ipapython.dn import DN -from ipapython.install.common import step -from ipapython.install.core import Knob from ipapython.ipa_log_manager import root_logger from ipapython.admintool import ScriptError from ipaplatform import services @@ -48,11 +45,11 @@ import SSSDConfig from subprocess import CalledProcessError from binascii import hexlify -from .common import BaseServer - if six.PY3: unicode = str +NoneType = type(None) + def get_dirman_password(): return installutils.read_password("Directory Manager (existing master)", @@ -1440,173 +1437,27 @@ def install(installer): services.knownservices.ipa.enable() -class Replica(BaseServer): - replica_file = Knob( - str, None, - description="a file generated by ipa-replica-prepare", - cli_positional=True, - cli_name='replica_file', - ) - - setup_ca = Knob(BaseServer.setup_ca) - setup_kra = Knob(BaseServer.setup_kra) - setup_dns = Knob(BaseServer.setup_dns) - - ip_addresses = Knob( - BaseServer.ip_addresses, - description=("Replica server IP Address. This option can be used " - "multiple times"), - ) - - dm_password = None - - password = Knob( - BaseServer.dm_password, - description=("Password to join the IPA realm. Assumes bulk password " - "unless principal is also set. (domain level 1+)\n" - "Directory Manager (existing master) password. " - "(domain level 0)"), - ) - - admin_password = Knob( - BaseServer.admin_password, - description="Kerberos password for the specified admin principal", - cli_short_name='w', - ) - - server = Knob( - str, None, - description="fully qualified name of IPA server to enroll to", - ) - - mkhomedir = Knob(BaseServer.mkhomedir) - no_host_dns = Knob(BaseServer.no_host_dns) - no_ntp = Knob(BaseServer.no_ntp) - no_pkinit = Knob(BaseServer.no_pkinit) - no_ui_redirect = Knob(BaseServer.no_ui_redirect) - ssh_trust_dns = Knob(BaseServer.ssh_trust_dns) - no_ssh = Knob(BaseServer.no_ssh) - no_sshd = Knob(BaseServer.no_sshd) - no_dns_sshfp = Knob(BaseServer.no_dns_sshfp) - - skip_conncheck = Knob( - bool, False, - description="skip connection check to remote master", - ) +def init(installer): + installer.unattended = not installer.interactive + installer.promote = installer.replica_file is None - principal = Knob( - str, None, - sensitive=True, - description="User Principal allowed to promote replicas " - "and join IPA realm", - cli_short_name='P', - ) - - keytab = Knob( - str, None, - description="path to backed up keytab from previous enrollment", - cli_short_name='k', - ) - - promote = False - - # ca - external_ca = None - external_ca_type = None - external_cert_files = None - ca_cert_files = None - subject = None - ca_signing_algorithm = None - - # dns - dnssec_master = None - disable_dnssec_master = None - kasp_db_file = None - force = None - zonemgr = None - - def __init__(self, **kwargs): - super(Replica, self).__init__(**kwargs) - - self._ccache = os.environ.get('KRB5CCNAME') - - self._top_dir = None - self._config = None - self._update_hosts_file = False - self._dirsrv_pkcs12_file = None - self._http_pkcs12_file = None - self._pkinit_pkcs12_file = None - self._dirsrv_pkcs12_info = None - self._http_pkcs12_info = None - self._pkinit_pkcs12_info = None - - # pylint: disable=no-member - - cert_file_req = (self.ca.dirsrv_cert_files, self.ca.http_cert_files) - cert_file_opt = (self.ca.pkinit_cert_files,) - - if self.replica_file is None: - self.promote = True - - if self.principal and not self.admin_password: - self.admin_password = self.password - self.password = None - - # If any of the PKCS#12 options are selected, all are required. - if any(cert_file_req + cert_file_opt) and not all(cert_file_req): - raise RuntimeError("--dirsrv-cert-file and --http-cert-file " - "are required if any PKCS#12 options are " - "used") - - if self.server and not self.domain_name: - raise RuntimeError("The --server option cannot be used " - "without providing domain via the --domain " - "option") - - else: - if not ipautil.file_exists(self.replica_file): - raise RuntimeError("Replica file %s does not exist" - % self.replica_file) - - if any(cert_file_req + cert_file_opt): - raise RuntimeError("You cannot specify any of " - "--dirsrv-cert-file, --http-cert-file, or " - "--pkinit-cert-file together with replica " - "file") - - CLIKnob = collections.namedtuple('CLIKnob', ('value', 'name')) - - conflicting_knobs = ( - CLIKnob(self.realm_name, '--realm'), - CLIKnob(self.domain_name, '--domain'), - CLIKnob(self.host_name, '--hostname'), - CLIKnob(self.server, '--server'), - CLIKnob(self.principal, '--principal'), - ) - - if any([k.value is not None for k in conflicting_knobs]): - conflicting_knob_names = [ - knob.name for knob in conflicting_knobs - if knob.value is not None - ] - - raise RuntimeError( - "You cannot specify '{0}' option(s) with replica file." - .format(", ".join(conflicting_knob_names)) - ) - - if self.setup_dns: - if (not self.dns.forwarders and not self.dns.no_forwarders - and not self.dns.auto_forwarders): - raise RuntimeError( - "You must specify at least one of --forwarder, " - "--auto-forwarders, or --no-forwarders options") - - @step() - def main(self): - if self.promote: - promote_check(self) - else: - install_check(self) - yield - install(self) + if installer.servers: + installer.server = installer.servers[0] + else: + installer.server = None + if installer.replica_file is None: + installer.password = installer.host_password + else: + installer.password = installer.dm_password + + installer._ccache = os.environ.get('KRB5CCNAME') + + installer._top_dir = None + installer._config = None + installer._update_hosts_file = False + installer._dirsrv_pkcs12_file = None + installer._http_pkcs12_file = None + installer._pkinit_pkcs12_file = None + installer._dirsrv_pkcs12_info = None + installer._http_pkcs12_info = None + installer._pkinit_pkcs12_info = None