mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
Merge pull request #4232 from tohojo/ipv6-support
IPv6 support for master-minion communication
This commit is contained in:
commit
ab5f540430
@ -19,7 +19,7 @@ from salt.version import __version__ # pylint: disable-msg=W402
|
||||
from salt.utils import migrations
|
||||
|
||||
try:
|
||||
from salt.utils import parsers
|
||||
from salt.utils import parsers, ip_bracket
|
||||
from salt.utils.verify import check_user, verify_env, verify_socket
|
||||
from salt.utils.verify import verify_files
|
||||
except ImportError as e:
|
||||
@ -82,6 +82,7 @@ class Master(parsers.MasterOptionParser):
|
||||
self.config['publish_port'],
|
||||
self.config['ret_port']):
|
||||
self.exit(4, 'The ports are not available to bind\n')
|
||||
self.config['interface'] = ip_bracket(self.config['interface'])
|
||||
migrations.migrate_paths(self.config)
|
||||
|
||||
# Late import so logging works correctly
|
||||
|
@ -371,6 +371,9 @@ class Publisher(multiprocessing.Process):
|
||||
except AttributeError:
|
||||
pub_sock.setsockopt(zmq.SNDHWM, 1)
|
||||
pub_sock.setsockopt(zmq.RCVHWM, 1)
|
||||
if hasattr(zmq, 'IPV4ONLY'):
|
||||
# IPv6 sockets work for both IPv6 and IPv4 addresses
|
||||
pub_sock.setsockopt(zmq.IPV4ONLY, 0)
|
||||
pub_uri = 'tcp://{interface}:{publish_port}'.format(**self.opts)
|
||||
# Prepare minion pull socket
|
||||
pull_sock = context.socket(zmq.PULL)
|
||||
@ -423,6 +426,9 @@ class ReqServer(object):
|
||||
# Prepare the zeromq sockets
|
||||
self.uri = 'tcp://{interface}:{ret_port}'.format(**self.opts)
|
||||
self.clients = self.context.socket(zmq.ROUTER)
|
||||
if hasattr(zmq, 'IPV4ONLY'):
|
||||
# IPv6 sockets work for both IPv6 and IPv4 addresses
|
||||
self.clients.setsockopt(zmq.IPV4ONLY, 0)
|
||||
self.workers = self.context.socket(zmq.DEALER)
|
||||
self.w_uri = 'ipc://{0}'.format(
|
||||
os.path.join(self.opts['sock_dir'], 'workers.ipc')
|
||||
|
@ -764,6 +764,9 @@ class Minion(object):
|
||||
self.socket = self.context.socket(zmq.SUB)
|
||||
self.socket.setsockopt(zmq.SUBSCRIBE, '')
|
||||
self.socket.setsockopt(zmq.IDENTITY, self.opts['id'])
|
||||
if hasattr(zmq, 'IPV4ONLY'):
|
||||
# IPv6 sockets work for both IPv6 and IPv4 addresses
|
||||
self.socket.setsockopt(zmq.IPV4ONLY, 0)
|
||||
if hasattr(zmq, 'RECONNECT_IVL_MAX'):
|
||||
self.socket.setsockopt(
|
||||
zmq.RECONNECT_IVL_MAX, self.opts['recon_max']
|
||||
|
@ -129,6 +129,9 @@ class SREQ(object):
|
||||
self.socket.setsockopt(
|
||||
zmq.RECONNECT_IVL_MAX, 5000
|
||||
)
|
||||
if hasattr(zmq, 'IPV4ONLY'):
|
||||
# IPv6 sockets work for both IPv6 and IPv4 addresses
|
||||
self.socket.setsockopt(zmq.IPV4ONLY, 0)
|
||||
self.socket.linger = linger
|
||||
if id_:
|
||||
self.socket.setsockopt(zmq.IDENTITY, id_)
|
||||
|
@ -304,34 +304,49 @@ def gen_mac(prefix='52:54:'):
|
||||
mac += random.choice(src) + random.choice(src) + ':'
|
||||
return mac[:-1]
|
||||
|
||||
def ip_bracket(addr):
|
||||
'''
|
||||
Convert IP address representation to ZMQ (url) format. ZMQ expects
|
||||
brackets around IPv6 literals, since they are used in URLs.
|
||||
'''
|
||||
if addr and addr.find(":") > -1 and not addr.startswith('['):
|
||||
return "[{0}]".format(addr)
|
||||
return addr
|
||||
|
||||
|
||||
def dns_check(addr, safe=False):
|
||||
'''
|
||||
Return the ip resolved by dns, but do not exit on failure, only raise an
|
||||
exception.
|
||||
exception. Obeys system preference for IPv4/6 address resolution.
|
||||
'''
|
||||
error = False
|
||||
try:
|
||||
socket.inet_aton(addr)
|
||||
except socket.error:
|
||||
# Not a valid ip adder, check DNS
|
||||
try:
|
||||
addr = socket.gethostbyname(addr)
|
||||
except socket.gaierror:
|
||||
err = ('This master address: \'{0}\' was previously resolvable '
|
||||
'but now fails to resolve! The previously resolved ip addr '
|
||||
'will continue to be used').format(addr)
|
||||
if safe:
|
||||
import salt.log
|
||||
if salt.log.is_console_configured():
|
||||
# If logging is not configured it also means that either
|
||||
# the master or minion instance calling this hasn't even
|
||||
# started running
|
||||
logging.getLogger(__name__).error(err)
|
||||
raise SaltClientError()
|
||||
else:
|
||||
err = err.format(addr)
|
||||
sys.stderr.write(err)
|
||||
sys.exit(42)
|
||||
hostnames = socket.getaddrinfo(addr, None, socket.AF_UNSPEC,
|
||||
socket.SOCK_STREAM)
|
||||
if not hostnames:
|
||||
error = True
|
||||
else:
|
||||
h = hostnames[0]
|
||||
addr = ip_bracket(h[4][0])
|
||||
except socket.gaierror:
|
||||
error = True
|
||||
|
||||
if error:
|
||||
err = ('This master address: \'{0}\' was previously resolvable '
|
||||
'but now fails to resolve! The previously resolved ip addr '
|
||||
'will continue to be used').format(addr)
|
||||
if safe:
|
||||
import salt.log
|
||||
if salt.log.is_console_configured():
|
||||
# If logging is not configured it also means that either
|
||||
# the master or minion instance calling this hasn't even
|
||||
# started running
|
||||
logging.getLogger(__name__).error(err)
|
||||
raise SaltClientError()
|
||||
else:
|
||||
err = err.format(addr)
|
||||
sys.stderr.write(err)
|
||||
sys.exit(42)
|
||||
return addr
|
||||
|
||||
|
||||
|
@ -83,13 +83,31 @@ def zmq_version():
|
||||
sys.stderr.write('CRITICAL {0}\n'.format(msg))
|
||||
return False
|
||||
|
||||
def lookup_family(hostname):
|
||||
'''
|
||||
Lookup a hostname and determine its address family. The first address returned
|
||||
will be AF_INET6 if the system is IPv6-enabled, and AF_INET otherwise.
|
||||
'''
|
||||
# If lookups fail, fall back to AF_INET sockets (and v4 addresses).
|
||||
fallback = socket.AF_INET
|
||||
try:
|
||||
hostnames = socket.getaddrinfo(hostname, None, socket.AF_UNSPEC,
|
||||
socket.SOCK_STREAM)
|
||||
if not hostnames:
|
||||
return fallback
|
||||
h = hostnames[0]
|
||||
return h[0]
|
||||
except socket.gaierror:
|
||||
return fallback
|
||||
|
||||
def verify_socket(interface, pub_port, ret_port):
|
||||
'''
|
||||
Attempt to bind to the sockets to verify that they are available
|
||||
'''
|
||||
pubsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
retsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
addr_family = lookup_family(interface)
|
||||
pubsock = socket.socket(addr_family, socket.SOCK_STREAM)
|
||||
retsock = socket.socket(addr_family, socket.SOCK_STREAM)
|
||||
try:
|
||||
pubsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
pubsock.bind((interface, int(pub_port)))
|
||||
|
@ -10,6 +10,7 @@ import stat
|
||||
import shutil
|
||||
import resource
|
||||
import tempfile
|
||||
import socket
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
@ -75,6 +76,10 @@ class TestVerify(TestCase):
|
||||
|
||||
def test_verify_socket(self):
|
||||
self.assertTrue(verify_socket('', 18000, 18001))
|
||||
if socket.has_ipv6:
|
||||
# Only run if Python is built with IPv6 support; otherwise
|
||||
# this will just fail.
|
||||
self.assertTrue(verify_socket('::', 18000, 18001))
|
||||
|
||||
@skipIf(os.environ.get('TRAVIS_PYTHON_VERSION', None) is not None,
|
||||
'Travis environment does not like too many open files')
|
||||
|
Loading…
Reference in New Issue
Block a user