Skip to content

Commit 6d80e39

Browse files
authored
Merge pull request #8649 from hanx-hep/fix-stomp-ipv4-only
fix (MQ): support IPv4-only broker hostnames
2 parents 65facc4 + 0de4f4f commit 6d80e39

2 files changed

Lines changed: 67 additions & 9 deletions

File tree

src/DIRAC/Resources/MessageQueue/StompMQConnector.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,14 @@ def setupConnection(self, parameters=None):
122122

123123
try:
124124
# Get IP addresses of brokers
125-
# Start with the IPv6, and randomize it
126-
ipv6_addrInfo = socket.getaddrinfo(host, port, socket.AF_INET6, socket.SOCK_STREAM)
127-
random.shuffle(ipv6_addrInfo)
128-
# Same with IPv4
129-
ipv4_addrInfo = socket.getaddrinfo(host, port, socket.AF_INET, socket.SOCK_STREAM)
130-
random.shuffle(ipv4_addrInfo)
131-
132-
# Create the host_port tuples, keeping the ipv6 in front
125+
addrInfo = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
126+
random.shuffle(addrInfo)
127+
# Prefer IPv6 while preserving the randomized order within each address family
128+
addrInfo.sort(key=lambda address: address[0] != socket.AF_INET6)
129+
130+
# Create the host_port tuples, keeping IPv6 in front
133131
host_and_ports = []
134-
for _family, _socktype, _proto, _canonname, sockaddr in ipv6_addrInfo + ipv4_addrInfo:
132+
for _family, _socktype, _proto, _canonname, sockaddr in addrInfo:
135133
host_and_ports.append((sockaddr[0], sockaddr[1]))
136134

137135
connectionArgs.update({"host_and_ports": host_and_ports})
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""Unit tests for the STOMP message queue connector."""
2+
3+
import socket
4+
from unittest import mock
5+
6+
from DIRAC.Resources.MessageQueue.StompMQConnector import StompMQConnector
7+
8+
9+
@mock.patch("DIRAC.Resources.MessageQueue.StompMQConnector.random.shuffle")
10+
@mock.patch("DIRAC.Resources.MessageQueue.StompMQConnector.stomp.Connection")
11+
@mock.patch("DIRAC.Resources.MessageQueue.StompMQConnector.socket.getaddrinfo")
12+
def test_setup_connection_with_ipv4_only(getaddrinfo, connection, _shuffle):
13+
getaddrinfo.return_value = [
14+
(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", ("192.0.2.1", 61613)),
15+
]
16+
17+
result = StompMQConnector().setupConnection({"Host": "mq.example", "VHost": "/"})
18+
19+
assert result["OK"]
20+
getaddrinfo.assert_called_once_with("mq.example", 61613, socket.AF_UNSPEC, socket.SOCK_STREAM)
21+
connection.assert_called_once_with(
22+
vhost="/",
23+
keepalive=True,
24+
timeout=60,
25+
heartbeats=(15_000, 15_000),
26+
reconnect_sleep_initial=1,
27+
reconnect_sleep_increase=0.5,
28+
reconnect_sleep_max=120,
29+
reconnect_sleep_jitter=0.1,
30+
reconnect_attempts_max=1e4,
31+
host_and_ports=[("192.0.2.1", 61613)],
32+
)
33+
34+
35+
@mock.patch("DIRAC.Resources.MessageQueue.StompMQConnector.random.shuffle")
36+
@mock.patch("DIRAC.Resources.MessageQueue.StompMQConnector.stomp.Connection")
37+
@mock.patch("DIRAC.Resources.MessageQueue.StompMQConnector.socket.getaddrinfo")
38+
def test_setup_connection_prefers_ipv6(getaddrinfo, connection, _shuffle):
39+
getaddrinfo.return_value = [
40+
(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", ("192.0.2.1", 61613)),
41+
(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", ("2001:db8::1", 61613, 0, 0)),
42+
]
43+
44+
result = StompMQConnector().setupConnection({"Host": "mq.example", "VHost": "/"})
45+
46+
assert result["OK"]
47+
assert connection.call_args.kwargs["host_and_ports"] == [
48+
("2001:db8::1", 61613),
49+
("192.0.2.1", 61613),
50+
]
51+
52+
53+
@mock.patch("DIRAC.Resources.MessageQueue.StompMQConnector.socket.getaddrinfo")
54+
def test_setup_connection_reports_resolution_failure(getaddrinfo):
55+
getaddrinfo.side_effect = socket.gaierror(socket.EAI_NONAME, "Name or service not known")
56+
57+
result = StompMQConnector().setupConnection({"Host": "missing.example", "VHost": "/"})
58+
59+
assert not result["OK"]
60+
assert "Name or service not known" in result["Message"]

0 commit comments

Comments
 (0)