Hi,
the attached patches implement another part of
<https://fedorahosted.org/freeipa/ticket/4468>.
Honza
--
Jan Cholasta
>From 9ebc623478065d582a449dde56f39ec927f12628 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 9 Jun 2015 11:33:13 +0000
Subject: [PATCH 1/4] install: Handle Knob cli_name and cli_aliases values
consistently
https://fedorahosted.org/freeipa/ticket/4468
---
ipapython/install/cli.py | 11 ++++++-----
ipaserver/install/server/install.py | 36 ++++++++++++++++++------------------
2 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/ipapython/install/cli.py b/ipapython/install/cli.py
index b83fd9a..e121f3d 100644
--- a/ipapython/install/cli.py
+++ b/ipapython/install/cli.py
@@ -114,8 +114,8 @@ class ConfigureTool(admintool.AdminTool):
short_opt_str = '-{0}'.format(knob_cls.cli_short_name)
else:
short_opt_str = ''
- cli_name = knob_cls.cli_name or name
- opt_str = '--{0}'.format(cli_name.replace('_', '-'))
+ cli_name = knob_cls.cli_name or name.replace('_', '-')
+ opt_str = '--{0}'.format(cli_name)
if not knob_cls.deprecated:
help = knob_cls.description
else:
@@ -127,8 +127,9 @@ class ConfigureTool(admintool.AdminTool):
)
if knob_cls.cli_aliases:
+ opt_strs = ['--{0}'.format(a) for a in knob_cls.cli_aliases]
opt_group.add_option(
- *knob_cls.cli_aliases,
+ *opt_strs,
help=optparse.SUPPRESS_HELP,
**kwargs
)
@@ -201,8 +202,8 @@ class ConfigureTool(admintool.AdminTool):
cfgr = transformed_cls(**kwargs)
except core.KnobValueError as e:
knob_cls = getattr(transformed_cls, e.name)
- cli_name = knob_cls.cli_name or e.name
- opt_str = '--{0}'.format(cli_name.replace('_', '-'))
+ cli_name = knob_cls.cli_name or e.name.replace('_', '-')
+ opt_str = '--{0}'.format(cli_name)
self.option_parser.error("option {0}: {1}".format(opt_str, e))
except RuntimeError as e:
self.option_parser.error(str(e))
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index 7a5aa3c..59a9d1e 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -1165,8 +1165,8 @@ class ServerCA(common.Installable, core.Group, core.Composite):
(list, str), None,
description=("File containing the IPA CA certificate and the external "
"CA certificate chain (can be specified multiple times)"),
- cli_name='external_cert_file',
- cli_aliases=['--external_cert_file', '--external_ca_file'],
+ cli_name='external-cert-file',
+ cli_aliases=['external_cert_file', 'external_ca_file'],
cli_metavar='FILE',
)
@@ -1184,8 +1184,8 @@ class ServerCA(common.Installable, core.Group, core.Composite):
(list, str), None,
description=("File containing the Directory Server SSL certificate "
"and private key"),
- cli_name='dirsrv_cert_file',
- cli_aliases=['--dirsrv_pkcs12'],
+ cli_name='dirsrv-cert-file',
+ cli_aliases=['dirsrv_pkcs12'],
cli_metavar='FILE',
)
@@ -1193,8 +1193,8 @@ class ServerCA(common.Installable, core.Group, core.Composite):
(list, str), None,
description=("File containing the Apache Server SSL certificate and "
"private key"),
- cli_name='http_cert_file',
- cli_aliases=['--http_pkcs12'],
+ cli_name='http-cert-file',
+ cli_aliases=['http_pkcs12'],
cli_metavar='FILE',
)
@@ -1202,8 +1202,8 @@ class ServerCA(common.Installable, core.Group, core.Composite):
(list, str), None,
description=("File containing the Kerberos KDC SSL certificate and "
"private key"),
- cli_name='pkinit_cert_file',
- cli_aliases=['--pkinit_pkcs12'],
+ cli_name='pkinit-cert-file',
+ cli_aliases=['pkinit_pkcs12'],
cli_metavar='FILE',
)
@@ -1211,7 +1211,7 @@ class ServerCA(common.Installable, core.Group, core.Composite):
str, None,
sensitive=True,
description="The password to unlock the Directory Server private key",
- cli_aliases=['--dirsrv_pin'],
+ cli_aliases=['dirsrv_pin'],
cli_metavar='PIN',
)
@@ -1219,7 +1219,7 @@ class ServerCA(common.Installable, core.Group, core.Composite):
str, None,
sensitive=True,
description="The password to unlock the Apache Server private key",
- cli_aliases=['--http_pin'],
+ cli_aliases=['http_pin'],
cli_metavar='PIN',
)
@@ -1227,7 +1227,7 @@ class ServerCA(common.Installable, core.Group, core.Composite):
str, None,
sensitive=True,
description="The password to unlock the Kerberos KDC private key",
- cli_aliases=['--pkinit_pin'],
+ cli_aliases=['pkinit_pin'],
cli_metavar='PIN',
)
@@ -1253,8 +1253,8 @@ class ServerCA(common.Installable, core.Group, core.Composite):
(list, str), None,
description=("File containing CA certificates for the service "
"certificate files"),
- cli_name='ca_cert_file',
- cli_aliases=['--root-ca-file'],
+ cli_name='ca-cert-file',
+ cli_aliases=['root-ca-file'],
cli_metavar='FILE',
)
@@ -1308,7 +1308,7 @@ class ServerDNS(common.Installable, core.Group, core.Composite):
(list, str), [],
description=("The reverse DNS zone to use. This option can be used "
"multiple times"),
- cli_name='reverse_zone',
+ cli_name='reverse-zone',
)
no_reverse = Knob(
@@ -1387,7 +1387,7 @@ class Server(common.Installable, common.Interactive, core.Composite):
str, None,
sensitive=True,
description="Directory Manager password",
- cli_name='ds_password',
+ cli_name='ds-password',
cli_short_name='p',
)
@@ -1428,7 +1428,7 @@ class Server(common.Installable, common.Interactive, core.Composite):
domainlevel = Knob(
int, constants.MAX_DOMAIN_LEVEL,
description="IPA domain level",
- cli_name='domain_level',
+ cli_name='domain-level',
)
@domainlevel.validator
@@ -1447,7 +1447,7 @@ class Server(common.Installable, common.Interactive, core.Composite):
(list, 'ip-local'), None,
description=("Master Server IP Address. This option can be used "
"multiple times"),
- cli_name='ip_address',
+ cli_name='ip-address',
)
no_ntp = Knob(
@@ -1473,7 +1473,7 @@ class Server(common.Installable, common.Interactive, core.Composite):
no_hbac_allow = Knob(
bool, False,
description="Don't install allow_all HBAC rule",
- cli_aliases=['--no_hbac_allow'],
+ cli_name='no_hbac_allow',
)
no_ui_redirect = Knob(
--
2.1.0
>From a7fe1a89a5c61bbb1a80eaa690e67be74a20d0fc Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 9 Jun 2015 11:41:09 +0000
Subject: [PATCH 2/4] install: Add support for positional arguments in CLI
tools
https://fedorahosted.org/freeipa/ticket/4468
---
ipapython/install/cli.py | 140 +++++++++++++++++++++++++++++++++++------------
1 file changed, 106 insertions(+), 34 deletions(-)
diff --git a/ipapython/install/cli.py b/ipapython/install/cli.py
index e121f3d..be7f218 100644
--- a/ipapython/install/cli.py
+++ b/ipapython/install/cli.py
@@ -19,12 +19,16 @@ __all__ = ['install_tool', 'uninstall_tool']
def install_tool(configurable_class, command_name, log_file_name,
- debug_option=False, uninstall_log_file_name=None):
- if uninstall_log_file_name is not None:
+ positional_arguments=None, debug_option=False,
+ uninstall_log_file_name=None,
+ uninstall_positional_arguments=None):
+ if (uninstall_log_file_name is not None or
+ uninstall_positional_arguments is not None):
uninstall_kwargs = dict(
configurable_class=configurable_class,
command_name=command_name,
log_file_name=uninstall_log_file_name,
+ positional_arguments=uninstall_positional_arguments,
debug_option=debug_option,
)
else:
@@ -37,6 +41,7 @@ def install_tool(configurable_class, command_name, log_file_name,
configurable_class=configurable_class,
command_name=command_name,
log_file_name=log_file_name,
+ positional_arguments=positional_arguments,
debug_option=debug_option,
uninstall_kwargs=uninstall_kwargs,
)
@@ -44,7 +49,7 @@ def install_tool(configurable_class, command_name, log_file_name,
def uninstall_tool(configurable_class, command_name, log_file_name,
- debug_option=False):
+ positional_arguments=None, debug_option=False):
return type(
'uninstall_tool({0})'.format(configurable_class.__name__),
(UninstallTool,),
@@ -52,6 +57,7 @@ def uninstall_tool(configurable_class, command_name, log_file_name,
configurable_class=configurable_class,
command_name=command_name,
log_file_name=log_file_name,
+ positional_arguments=positional_arguments,
debug_option=debug_option,
)
)
@@ -60,6 +66,7 @@ def uninstall_tool(configurable_class, command_name, log_file_name,
class ConfigureTool(admintool.AdminTool):
configurable_class = None
debug_option = False
+ positional_arguments = None
@staticmethod
def _transform(configurable_class):
@@ -77,6 +84,8 @@ class ConfigureTool(admintool.AdminTool):
knob_cls = getattr(owner_cls, name)
if not knob_cls.initializable:
continue
+ if cls.positional_arguments and name in cls.positional_arguments:
+ continue
group_cls = owner_cls.group()
try:
@@ -88,17 +97,6 @@ class ConfigureTool(admintool.AdminTool):
kwargs = dict()
if knob_cls.type is bool:
kwargs['type'] = None
- elif knob_cls.type is int:
- kwargs['type'] = 'int'
- elif knob_cls.type is long:
- kwargs['type'] = 'long'
- elif knob_cls.type is float:
- kwargs['type'] = 'float'
- elif knob_cls.type is complex:
- kwargs['type'] = 'complex'
- elif isinstance(knob_cls.type, set):
- kwargs['type'] = 'choice'
- kwargs['choices'] = list(knob_cls.type)
else:
kwargs['type'] = 'string'
kwargs['dest'] = name
@@ -150,41 +148,110 @@ class ConfigureTool(admintool.AdminTool):
debug_option=cls.debug_option)
@classmethod
- def _option_callback(cls, option, opt_str, value, parser, knob):
- if knob.type is bool:
- value_type = bool
+ def _option_callback(cls, option, opt_str, value, parser, knob_cls):
+ old_value = getattr(parser.values, option.dest, None)
+ try:
+ value = cls._parse_knob(knob_cls, old_value, value)
+ except ValueError as e:
+ raise optparse.OptionValueError(
+ "option {0}: {1}".format(opt_str, e))
+
+ setattr(parser.values, option.dest, value)
+
+ @classmethod
+ def _parse_knob(cls, knob_cls, old_value, value):
+ if knob_cls.type is bool:
+ parse = bool
is_list = False
value = True
else:
- if isinstance(knob.type, tuple):
- assert knob.type[0] is list
- value_type = knob.type[1]
+ if isinstance(knob_cls.type, tuple):
+ assert knob_cls.type[0] is list
+ value_type = knob_cls.type[1]
is_list = True
else:
- value_type = knob.type
+ value_type = knob_cls.type
is_list = False
- if value_type == 'ip':
- value_type = CheckedIPAddress
+ if value_type is int:
+ def parse(value):
+ try:
+ return int(value, 0)
+ except ValueError:
+ raise ValueError(
+ "invalid integer value: {0}".format(repr(value)))
+ elif value_type is long:
+ def parse(value):
+ try:
+ return long(value, 0)
+ except ValueError:
+ raise ValueError(
+ "invalid long integer value: {0}".format(
+ repr(value)))
+ elif value_type == 'ip':
+ def parse(value):
+ try:
+ return CheckedIPAddress(value)
+ except Exception as e:
+ raise ValueError("invalid IP address {0}: {1}".format(
+ value, e))
elif value_type == 'ip-local':
- value_type = lambda v: CheckedIPAddress(v, match_local=True)
+ def parse(value):
+ try:
+ return CheckedIPAddress(value, match_local=True)
+ except Exception as e:
+ raise ValueError("invalid IP address {0}: {1}".format(
+ value, e))
+ elif isinstance(value_type, set):
+ def parse(value):
+ if value not in value_type:
+ raise ValueError(
+ "invalid choice {0} (choose from {1})".format(
+ repr(value), ', '.join(repr(value_type))))
+ return value
+ else:
+ parse = value_type
- try:
- value = value_type(value)
- except ValueError as e:
- raise optparse.OptionValueError(
- "option {0}: {1}".format(opt_str, e))
+ value = parse(value)
if is_list:
- old_value = getattr(parser.values, option.dest) or []
+ old_value = old_value or []
old_value.append(value)
value = old_value
- setattr(parser.values, option.dest, value)
+ return value
def validate_options(self, needs_root=True):
super(ConfigureTool, self).validate_options(needs_root=needs_root)
+ if self.positional_arguments:
+ if len(self.args) > len(self.positional_arguments):
+ self.option_parser.error("Too many arguments provided")
+
+ index = 0
+
+ transformed_cls = self._transform(self.configurable_class)
+ for owner_cls, name in transformed_cls.knobs():
+ knob_cls = getattr(owner_cls, name)
+ if name not in self.positional_arguments:
+ continue
+
+ try:
+ value = self.args[index]
+ except IndexError:
+ break
+
+ old_value = getattr(self.options, name, None)
+ try:
+ value = self._parse_knob(knob_cls, old_value, value)
+ except ValueError as e:
+ self.option_parser.error(
+ "argument {0}: {1}".format(index + 1, e))
+
+ setattr(self.options, name, value)
+
+ index += 1
+
def run(self):
kwargs = {}
@@ -202,9 +269,14 @@ class ConfigureTool(admintool.AdminTool):
cfgr = transformed_cls(**kwargs)
except core.KnobValueError as e:
knob_cls = getattr(transformed_cls, e.name)
- cli_name = knob_cls.cli_name or e.name.replace('_', '-')
- opt_str = '--{0}'.format(cli_name)
- self.option_parser.error("option {0}: {1}".format(opt_str, e))
+ try:
+ index = self.positional_arguments.index(e.name)
+ except IndexError:
+ cli_name = knob_cls.cli_name or e.name.replace('_', '-')
+ desc = "option --{0}".format(cli_name)
+ else:
+ desc = "argument {0}".format(index + 1)
+ self.option_parser.error("{0}: {1}".format(desc, e))
except RuntimeError as e:
self.option_parser.error(str(e))
--
2.1.0
>From 71a8725c1f668428a75c7f7ad0bddf1e0d36d88c Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 9 Jun 2015 11:42:20 +0000
Subject: [PATCH 3/4] install: Allow setting usage in CLI tools
https://fedorahosted.org/freeipa/ticket/4468
---
ipapython/install/cli.py | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/ipapython/install/cli.py b/ipapython/install/cli.py
index be7f218..b526ea7 100644
--- a/ipapython/install/cli.py
+++ b/ipapython/install/cli.py
@@ -19,16 +19,18 @@ __all__ = ['install_tool', 'uninstall_tool']
def install_tool(configurable_class, command_name, log_file_name,
- positional_arguments=None, debug_option=False,
+ positional_arguments=None, usage=None, debug_option=False,
uninstall_log_file_name=None,
- uninstall_positional_arguments=None):
+ uninstall_positional_arguments=None, uninstall_usage=None):
if (uninstall_log_file_name is not None or
- uninstall_positional_arguments is not None):
+ uninstall_positional_arguments is not None or
+ uninstall_usage is not None):
uninstall_kwargs = dict(
configurable_class=configurable_class,
command_name=command_name,
log_file_name=uninstall_log_file_name,
positional_arguments=uninstall_positional_arguments,
+ usage=uninstall_usage,
debug_option=debug_option,
)
else:
@@ -42,6 +44,7 @@ def install_tool(configurable_class, command_name, log_file_name,
command_name=command_name,
log_file_name=log_file_name,
positional_arguments=positional_arguments,
+ usage=usage,
debug_option=debug_option,
uninstall_kwargs=uninstall_kwargs,
)
@@ -49,7 +52,7 @@ def install_tool(configurable_class, command_name, log_file_name,
def uninstall_tool(configurable_class, command_name, log_file_name,
- positional_arguments=None, debug_option=False):
+ positional_arguments=None, usage=None, debug_option=False):
return type(
'uninstall_tool({0})'.format(configurable_class.__name__),
(UninstallTool,),
@@ -58,6 +61,7 @@ def uninstall_tool(configurable_class, command_name, log_file_name,
command_name=command_name,
log_file_name=log_file_name,
positional_arguments=positional_arguments,
+ usage=usage,
debug_option=debug_option,
)
)
--
2.1.0
>From 15fa38932bdc68b895f7b63fec526292f9cad864 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 9 Jun 2015 11:42:50 +0000
Subject: [PATCH 4/4] install: Migrate ipa-replica-install to the install
framework
https://fedorahosted.org/freeipa/ticket/4468
---
install/tools/ipa-replica-install | 151 +-------------
ipaserver/install/server/__init__.py | 3 +-
ipaserver/install/server/replicainstall.py | 322 +++++++++++++++++++++++------
3 files changed, 275 insertions(+), 201 deletions(-)
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 791a272..10a1082 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -18,148 +18,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-import sys
-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 ipapython.install import cli
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():
- usage = "%prog [options] REPLICA_FILE"
- parser = IPAOptionParser(usage=usage, version=version.VERSION)
-
- basic_group = OptionGroup(parser, "basic options")
- basic_group.add_option("--setup-ca", dest="setup_ca", action="store_true",
- default=False, help="configure a dogtag CA")
- 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():
- 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)
+ReplicaInstall = cli.install_tool(
+ Replica,
+ command_name='ipa-replica-install',
+ positional_arguments='replica_file',
+ usage='%prog [options] REPLICA_FILE',
+ log_file_name=paths.IPAREPLICA_INSTALL_LOG,
+ debug_option=True,
+)
-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)
+ReplicaInstall.run_cli()
diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py
index a6db965..da2ceec 100644
--- a/ipaserver/install/server/__init__.py
+++ b/ipaserver/install/server/__init__.py
@@ -3,7 +3,6 @@
#
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
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index 149d6b4..9455117 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -14,6 +14,9 @@ import tempfile
from ipapython import dogtag, ipautil, sysrestore
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 ipaplatform import services
from ipaplatform.tasks import tasks
@@ -28,7 +31,6 @@ from ipaserver.install.replication import (
ReplicationManager, replica_conn_check)
DIRMAN_DN = DN(('cn', 'directory manager'))
-REPLICA_INFO_TOP_DIR = None
def get_dirman_password():
@@ -162,7 +164,7 @@ def install_dns_records(config, options, remote_api):
config.realm_name,
config.domain_name,
reverse_zone,
- options.conf_ntp,
+ not options.no_ntp,
options.setup_ca)
except errors.NotFound, e:
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
-def remove_replica_info_dir():
+def remove_replica_info_dir(installer):
# always try to remove decrypted replica file
try:
- if REPLICA_INFO_TOP_DIR:
- shutil.rmtree(REPLICA_INFO_TOP_DIR)
+ if installer._top_dir is not None:
+ shutil.rmtree(installer._top_dir)
except OSError:
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 decorated(*args, **kwargs):
+ def decorated(installer):
try:
try:
- func(*args, **kwargs)
+ func(installer)
except BaseException:
- destroy_private_ccache()
- remove_replica_info_dir()
+ remove_replica_info_dir(installer)
raise
except KeyboardInterrupt:
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
@common_cleanup
-def install_check(filename, options):
- global config
-
- init_private_ccache()
+def install_check(installer):
+ options = installer
+ filename = installer.replica_file
tasks.check_selinux_status()
@@ -339,10 +319,8 @@ def install_check(filename, options):
"Please uninstall it first before configuring the replica, "
"using 'ipa-client-install --uninstall'.")
- global sstore
sstore = sysrestore.StateFile(paths.SYSRESTORE)
- global fstore
fstore = sysrestore.FileStore(paths.SYSRESTORE)
# Check to see if httpd is already configured to listen on 443
@@ -351,7 +329,7 @@ def install_check(filename, options):
check_dirsrv()
- if options.conf_ntp:
+ if not options.no_ntp:
try:
ipaclient.ntpconf.check_timedate_services()
except ipaclient.ntpconf.NTPConflictingService, e:
@@ -373,8 +351,7 @@ def install_check(filename, options):
sys.exit("Directory Manager password required")
config = create_replica_config(dirman_password, filename, options)
- global REPLICA_INFO_TOP_DIR
- REPLICA_INFO_TOP_DIR = config.top_dir
+ installer._top_dir = config.top_dir
config.setup_ca = options.setup_ca
config.setup_kra = options.setup_kra
@@ -398,7 +375,7 @@ def install_check(filename, options):
dns.install_check(False, True, options, config.host_name)
else:
installutils.get_server_ip_address(config.host_name, fstore,
- options.unattended, False,
+ not installer.interactive, False,
options.ip_addresses)
# check connection
@@ -407,18 +384,22 @@ def install_check(filename, options):
config.master_host_name, config.host_name, config.realm_name,
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"
if not ipautil.file_exists(cafile):
raise RuntimeError("CA cert file is not available. Please run "
"ipa-replica-prepare to create a new replica file.")
+ installer._fstore = fstore
+ installer._sstore = sstore
+ installer._config = config
+
@common_cleanup
-def install(filename, options):
- global config
+def install(installer):
+ options = installer
+ fstore = installer._fstore
+ sstore = installer._sstore
+ config = installer._config
dogtag_constants = dogtag.install_constants
@@ -544,7 +525,7 @@ def install(filename, options):
resolution_ok = (
check_dns_resolution(master, dns_masters) and
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):
sys.exit(0)
else:
@@ -562,7 +543,7 @@ def install(filename, options):
replman.conn.unbind()
# Configure ntpd
- if options.conf_ntp:
+ if not options.no_ntp:
ipaclient.ntpconf.force_ntpd(sstore)
ntp = ntpinstance.NTPInstance()
ntp.create_instance()
@@ -584,8 +565,8 @@ def install(filename, options):
ca.install(False, config, options)
- krb = install_krb(config, setup_pkinit=options.setup_pkinit)
- http = install_http(config, auto_redirect=options.ui_redirect)
+ krb = install_krb(config, setup_pkinit=not options.no_pkinit)
+ http = install_http(config, auto_redirect=not options.no_ui_redirect)
otpd = otpdinstance.OtpdInstance()
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",
"--domain", config.domain_name, "--server", config.host_name,
"--realm", config.realm_name]
- if not options.create_sshfp:
+ if options.no_dns_sshfp:
args.append("--no-dns-sshfp")
- if options.trust_sshfp:
+ if options.ssh_trust_dns:
args.append("--ssh-trust-dns")
- if not options.conf_ssh:
+ if options.no_ssh:
args.append("--no-ssh")
- if not options.conf_sshd:
+ if options.no_sshd:
args.append("--no-sshd")
if options.mkhomedir:
args.append("--mkhomedir")
@@ -646,5 +627,228 @@ def install(filename, options):
# Everything installed properly, activate ipa service.
services.knownservices.ipa.enable()
- destroy_private_ccache()
- remove_replica_info_dir()
+ remove_replica_info_dir(installer)
+
+
+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)
--
2.1.0
--
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code