Merge pull request #39289 from bobrik/autodetect-ipv6

Autodetect IPv6 connectivity from minion to master
This commit is contained in:
Mike Place 2017-02-22 12:05:32 -07:00 committed by GitHub
commit c10965833a
6 changed files with 37 additions and 17 deletions

View file

@ -81,9 +81,10 @@ The option can can also be set to a list of masters, enabling
``ipv6``
--------
Default: ``False``
Default: ``None``
Whether the master should be connected over IPv6.
Whether the master should be connected over IPv6. By default salt minion
will try to automatically detect IPv6 connectivity to master.
.. code-block:: yaml

View file

@ -990,7 +990,7 @@ DEFAULT_MINION_OPTS = {
'mine_interval': 60,
'ipc_mode': _DFLT_IPC_MODE,
'ipc_write_buffer': _DFLT_IPC_WBUFFER,
'ipv6': False,
'ipv6': None,
'file_buffer_size': 262144,
'tcp_pub_port': 4510,
'tcp_pull_port': 4511,

View file

@ -132,7 +132,7 @@ log = logging.getLogger(__name__)
# 6. Handle publications
def resolve_dns(opts, fallback=True):
def resolve_dns(opts, fallback=True, connect=True):
'''
Resolves the master_ip and master_uri options
'''
@ -149,13 +149,13 @@ def resolve_dns(opts, fallback=True):
if opts['master'] == '':
raise SaltSystemExit
ret['master_ip'] = \
salt.utils.dns_check(opts['master'], True, opts['ipv6'])
salt.utils.dns_check(opts['master'], opts['master_port'], True, opts['ipv6'], connect)
except SaltClientError:
if opts['retry_dns']:
while True:
import salt.log
msg = ('Master hostname: \'{0}\' not found. Retrying in {1} '
'seconds').format(opts['master'], opts['retry_dns'])
msg = ('Master hostname: \'{0}\' not found or not responsive. '
'Retrying in {1} seconds').format(opts['master'], opts['retry_dns'])
if salt.log.setup.is_console_configured():
log.error(msg)
else:
@ -163,7 +163,7 @@ def resolve_dns(opts, fallback=True):
time.sleep(opts['retry_dns'])
try:
ret['master_ip'] = salt.utils.dns_check(
opts['master'], True, opts['ipv6']
opts['master'], opts['master_port'], True, opts['ipv6'], connect
)
break
except SaltClientError:

View file

@ -346,7 +346,7 @@ class AsyncZeroMQPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.t
zmq.RECONNECT_IVL_MAX, self.opts['recon_max']
)
if self.opts['ipv6'] is True and hasattr(zmq, 'IPV4ONLY'):
if (self.opts['ipv6'] is True or ':' in self.opts['master_ip']) and hasattr(zmq, 'IPV4ONLY'):
# IPv6 sockets work for both IPv6 and IPv4 addresses
self._socket.setsockopt(zmq.IPV4ONLY, 0)

View file

@ -716,10 +716,11 @@ def ip_bracket(addr):
return addr
def dns_check(addr, safe=False, ipv6=False):
def dns_check(addr, port, safe=False, ipv6=None, connect=True):
'''
Return the ip resolved by dns, but do not exit on failure, only raise an
exception. Obeys system preference for IPv4/6 address resolution.
Tries to connect to the address before considering it useful.
'''
error = False
try:
@ -732,12 +733,30 @@ def dns_check(addr, safe=False, ipv6=False):
if not hostnames:
error = True
else:
addr = False
resolved = False
for h in hostnames:
if h[0] == socket.AF_INET or (h[0] == socket.AF_INET6 and ipv6):
addr = ip_bracket(h[4][0])
if h[0] == socket.AF_INET and ipv6 is True:
continue
if h[0] == socket.AF_INET6 and ipv6 is False:
continue
if h[0] == socket.AF_INET6 and connect is False and ipv6 is None:
continue
candidate_addr = ip_bracket(h[4][0])
if not connect:
resolved = candidate_addr
s = socket.socket(h[0], socket.SOCK_STREAM)
try:
s.connect((candidate_addr.strip('[]'), port))
s.close()
resolved = candidate_addr
break
if not addr:
except socket.error:
pass
if not resolved:
error = True
except TypeError:
err = ('Attempt to resolve address \'{0}\' failed. Invalid or unresolveable address').format(addr)
@ -746,7 +765,7 @@ def dns_check(addr, safe=False, ipv6=False):
error = True
if error:
err = ('DNS lookup of \'{0}\' failed.').format(addr)
err = ('DNS lookup or connection check of \'{0}\' failed.').format(addr)
if safe:
if salt.log.is_console_configured():
# If logging is not configured it also means that either
@ -755,7 +774,7 @@ def dns_check(addr, safe=False, ipv6=False):
log.error(err)
raise SaltClientError()
raise SaltSystemExit(code=42, msg=err)
return addr
return resolved
def required_module_list(docstring=None):

View file

@ -364,7 +364,7 @@ class ConfigTestCase(TestCase, integration.AdaptedConfigurationTestCaseMixIn):
syndic_opts = sconfig.syndic_config(
syndic_conf_path, minion_conf_path
)
syndic_opts.update(salt.minion.resolve_dns(syndic_opts))
syndic_opts.update(salt.minion.resolve_dns(syndic_opts, connect=False))
root_dir = syndic_opts['root_dir']
# id & pki dir are shared & so configured on the minion side
self.assertEqual(syndic_opts['id'], 'minion')