mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-10-25 06:38:02 -05:00
Allow ipa tool to force specific server
Fixes: https://pagure.io/freeipa/issue/9744 Signed-off-by: Aleksandr Sharov <asharov@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Florence Blanc-Renaud <flo@redhat.com> Reviewed-By: David Hanina <dhanina@redhat.com>
This commit is contained in:
1 parent
a66df6d7b4
commit
d9a2a6abcd
5 files changed
+124
-39
No files matched your search
@@ -55,6 +55,9 @@ Prompt for all parameters of \fICOMMAND\fR, even if they are optional.
|
||||
\fB\-f\fR, \fB\-\-no\-fallback\fR
|
||||
Don't fall back to other IPA servers if the default doesn't work.
|
||||
.TP
|
||||
\fB\-\-force\-server\fR \fIfqdn\fR
|
||||
Specify the IPA server to connect to. Overrides the default server in \fI/etc/ipa/default.conf\fR, does not fall back to SRV if server doesn\'t reply. Requires fully-qualified domain name.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Produce verbose output. A second -v pretty-prints the JSON request and response. A third \-v displays the HTTP request and response.
|
||||
.TP
|
||||
|
||||
+57
-37
@@ -536,17 +536,20 @@ class Env:
|
||||
|
||||
1. Call `Env._bootstrap()` if it hasn't already been called.
|
||||
|
||||
2. Merge-in variables from the configuration file ``self.conf``
|
||||
2. If server is forced, derive URIs from it by calling
|
||||
`Env._derive_uris()`.
|
||||
|
||||
3. Merge-in variables from the configuration file ``self.conf``
|
||||
(if it exists) by calling `Env._merge_from_file()`.
|
||||
|
||||
3. Merge-in variables from the defaults configuration file
|
||||
4. Merge-in variables from the defaults configuration file
|
||||
``self.conf_default`` (if it exists) by calling
|
||||
`Env._merge_from_file()`.
|
||||
|
||||
4. Intelligently fill-in the *in_server* , *logdir*, *log*, and
|
||||
*jsonrpc_uri* variables if they haven't already been set.
|
||||
5. Intelligently fill-in the *in_server* , *logdir*, *log*
|
||||
variables and derive URIs if they haven't already been set.
|
||||
|
||||
5. Merge-in the variables in ``defaults`` by calling `Env._merge()`.
|
||||
6. Merge-in the variables in ``defaults`` by calling `Env._merge()`.
|
||||
In normal circumstances ``defaults`` will simply be those
|
||||
specified in `constants.DEFAULT_CONFIG`.
|
||||
|
||||
@@ -565,6 +568,13 @@ class Env:
|
||||
self.__doing('_finalize_core')
|
||||
self.__do_if_not_done('_bootstrap')
|
||||
|
||||
# If server is forced, set it up and derive URIs from it:
|
||||
uris_derived = False
|
||||
if 'forced_server' in self and 'server' not in self:
|
||||
self.server = self["forced_server"]
|
||||
self._derive_uris(**defaults)
|
||||
uris_derived = True
|
||||
|
||||
# Merge in context config file and then default config file:
|
||||
mode = self.__d.get('mode') # pylint: disable=no-member
|
||||
# documented public modes: production, developer
|
||||
@@ -601,38 +611,9 @@ class Env:
|
||||
if 'basedn' not in self and 'domain' in self:
|
||||
self.basedn = DN(*(('dc', dc) for dc in self.domain.split('.')))
|
||||
|
||||
# Derive xmlrpc_uri from server
|
||||
# (Note that this is done before deriving jsonrpc_uri from xmlrpc_uri
|
||||
# and server from jsonrpc_uri so that when only server or xmlrpc_uri
|
||||
# is specified, all 3 keys have a value.)
|
||||
if 'xmlrpc_uri' not in self and 'server' in self:
|
||||
self.xmlrpc_uri = 'https://{}/ipa/xml'.format(self.server)
|
||||
|
||||
# Derive ldap_uri from server
|
||||
if 'ldap_uri' not in self and 'server' in self:
|
||||
self.ldap_uri = 'ldap://{}'.format(self.server)
|
||||
|
||||
# Derive jsonrpc_uri from xmlrpc_uri
|
||||
if 'jsonrpc_uri' not in self:
|
||||
if 'xmlrpc_uri' in self:
|
||||
xmlrpc_uri = self.xmlrpc_uri
|
||||
else:
|
||||
xmlrpc_uri = defaults.get('xmlrpc_uri')
|
||||
if xmlrpc_uri:
|
||||
(scheme, netloc, uripath, params, query, fragment
|
||||
) = urlparse(xmlrpc_uri)
|
||||
uripath = uripath.replace('/xml', '/json', 1)
|
||||
self.jsonrpc_uri = urlunparse((
|
||||
scheme, netloc, uripath, params, query, fragment))
|
||||
|
||||
if 'server' not in self:
|
||||
if 'jsonrpc_uri' in self:
|
||||
jsonrpc_uri = self.jsonrpc_uri
|
||||
else:
|
||||
jsonrpc_uri = defaults.get('jsonrpc_uri')
|
||||
if jsonrpc_uri:
|
||||
parsed = urlparse(jsonrpc_uri)
|
||||
self.server = parsed.netloc
|
||||
# Derive URIs if not already done:
|
||||
if not uris_derived:
|
||||
self._derive_uris(**defaults)
|
||||
|
||||
self._merge(**defaults)
|
||||
|
||||
@@ -666,6 +647,45 @@ class Env:
|
||||
"tls_version_min is set to a higher TLS version than "
|
||||
"tls_version_max.")
|
||||
|
||||
def _derive_uris(self, **defaults):
|
||||
"""
|
||||
Intelligently derive URIs from other variables
|
||||
"""
|
||||
# Derive xmlrpc_uri from server
|
||||
# (Note that this is done before deriving jsonrpc_uri from xmlrpc_uri
|
||||
# and server from jsonrpc_uri so that when only server or xmlrpc_uri
|
||||
# is specified, all 3 keys have a value.)
|
||||
if 'xmlrpc_uri' not in self and 'server' in self:
|
||||
self.xmlrpc_uri = 'https://{}/ipa/xml'.format(self.server)
|
||||
|
||||
# Derive ldap_uri from server
|
||||
if 'ldap_uri' not in self and 'server' in self:
|
||||
self.ldap_uri = 'ldap://{}'.format(self.server)
|
||||
|
||||
# Derive jsonrpc_uri from xmlrpc_uri
|
||||
if 'jsonrpc_uri' not in self:
|
||||
if 'xmlrpc_uri' in self:
|
||||
xmlrpc_uri = self.xmlrpc_uri
|
||||
else:
|
||||
xmlrpc_uri = defaults.get('xmlrpc_uri')
|
||||
if xmlrpc_uri:
|
||||
(scheme, netloc, uripath, params, query, fragment) = urlparse(
|
||||
xmlrpc_uri
|
||||
)
|
||||
uripath = uripath.replace("/xml", "/json", 1)
|
||||
self.jsonrpc_uri = urlunparse(
|
||||
(scheme, netloc, uripath, params, query, fragment)
|
||||
)
|
||||
|
||||
if 'server' not in self:
|
||||
if 'jsonrpc_uri' in self:
|
||||
jsonrpc_uri = self.jsonrpc_uri
|
||||
else:
|
||||
jsonrpc_uri = defaults.get('jsonrpc_uri')
|
||||
if jsonrpc_uri:
|
||||
parsed = urlparse(jsonrpc_uri)
|
||||
self.server = parsed.netloc
|
||||
|
||||
def _finalize(self, **lastchance):
|
||||
"""
|
||||
Finalize and lock environment.
|
||||
|
||||
+13
-1
@@ -562,6 +562,15 @@ class API(ReadOnly):
|
||||
callback=config_file_callback, type='string',
|
||||
help='Load configuration from FILE.',
|
||||
)
|
||||
parser.add_option(
|
||||
'--force-server',
|
||||
dest='forced_server',
|
||||
metavar='FQDN',
|
||||
type='string',
|
||||
help='Specify the IPA server to connect to. Overrides the default '
|
||||
'server in /etc/ipa/default.conf, does not fall back to SRV if '
|
||||
'server doesn\'t reply',
|
||||
)
|
||||
parser.add_option('-d', '--debug', action='store_true',
|
||||
help='Produce full debuging output',
|
||||
)
|
||||
@@ -607,10 +616,13 @@ class API(ReadOnly):
|
||||
raise errors.OptionError(_('Unable to parse option {item}'
|
||||
.format(item=item)))
|
||||
for key in ('conf', 'debug', 'verbose', 'prompt_all', 'interactive',
|
||||
'fallback', 'delegate'):
|
||||
'forced_server', 'fallback', 'delegate'):
|
||||
value = getattr(options, key, None)
|
||||
if value is not None:
|
||||
overrides[key] = value
|
||||
if key == 'forced_server':
|
||||
if getattr(options, 'fallback', None) is None:
|
||||
overrides['fallback'] = False
|
||||
if hasattr(options, 'prod'):
|
||||
overrides['webui_prod'] = options.prod
|
||||
if context is not None:
|
||||
|
||||
+9
-1
@@ -427,7 +427,15 @@ class SSLTransport(LanguageAwareTransport):
|
||||
tls_version_min=api.env.tls_version_min,
|
||||
tls_version_max=api.env.tls_version_max)
|
||||
|
||||
conn.connect()
|
||||
try:
|
||||
conn.connect()
|
||||
except socket.error as e:
|
||||
# Network error, close connection and let transport
|
||||
# handle reconnect for us.
|
||||
self.close()
|
||||
logger.debug("HTTP connection destroyed (%s)", host)
|
||||
raise errors.NetworkError(message=unicode(e))
|
||||
|
||||
logger.debug("New HTTP connection (%s)", host)
|
||||
|
||||
self._connection = host, conn
|
||||
|
||||
@@ -1813,6 +1813,48 @@ class TestIPACommand(IntegrationTest):
|
||||
assert f"{interm_nick} {intermediate_serial}" not in certs
|
||||
assert f"{interm_nick} {duplicate_serial}" in certs
|
||||
|
||||
def test_ipa_force_server(self):
|
||||
"""
|
||||
Test ipa command with --force-server option
|
||||
Do ping to a replica from the master, verify that the ipa tool
|
||||
connected to the replica
|
||||
"""
|
||||
tasks.kinit_admin(self.master)
|
||||
replica = self.replicas[0]
|
||||
|
||||
result = self.master.run_command(
|
||||
["ipa", "--force-server", replica.hostname, "--debug", "ping"],
|
||||
)
|
||||
assert replica.hostname in result.stderr_text
|
||||
|
||||
# test with invalid server
|
||||
failresult = self.master.run_command(
|
||||
["ipa",
|
||||
"--force-server",
|
||||
'1' + str(replica.hostname),
|
||||
"--debug",
|
||||
"ping"],
|
||||
raiseonerr=False
|
||||
)
|
||||
assert '[Errno -2] Name or service not known' in failresult.stderr_text
|
||||
|
||||
# test with not running valid server
|
||||
self.master.run_command(
|
||||
["systemctl", "stop", "ipa"]
|
||||
)
|
||||
failresult = self.master.run_command(
|
||||
["ipa",
|
||||
"--force-server",
|
||||
self.master.hostname,
|
||||
"--debug",
|
||||
"ping"],
|
||||
raiseonerr=False
|
||||
)
|
||||
assert '[Errno 111] Connection refused' in failresult.stderr_text
|
||||
self.master.run_command(
|
||||
["systemctl", "start", "ipa"]
|
||||
)
|
||||
|
||||
def test_expiration_date_post_2038(self, expire_password):
|
||||
"""Test that expiration dates after 2038 function without
|
||||
overflow.
|
||||
|
||||
Reference in new issue
Block a user