Fix dns.query.tcp/udp not always handling ipv6 properly
Created a new generic send_dns_msg that properly handles both ip and hostnames and fully supports ipv4 and ipv6. Also, moved all usage of dns.query.tcp/udp to a central location. Change-Id: I403ed6716b3ceffa1910269adf0e352f75e9dd5b
This commit is contained in:
parent
7237c3d665
commit
acd930d342
@ -32,18 +32,16 @@ import dns.opcode
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import eventlet
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate.backend import base
|
||||
import designate.backend.private_codes as pcodes
|
||||
from designate.backend import private_codes
|
||||
from designate.conf.agent import DEFAULT_AGENT_PORT
|
||||
from designate import dnsutils
|
||||
from designate import exceptions
|
||||
from designate.mdns import rpcapi as mdns_api
|
||||
|
||||
dns_query = eventlet.import_patched('dns.query')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -72,9 +70,9 @@ class AgentPoolBackend(base.Backend):
|
||||
response, retry = self._make_and_send_dns_message(
|
||||
zone.name,
|
||||
self.timeout,
|
||||
pcodes.CC,
|
||||
pcodes.CREATE,
|
||||
pcodes.CLASSCC,
|
||||
private_codes.CC,
|
||||
private_codes.CREATE,
|
||||
private_codes.CLASSCC,
|
||||
self.host,
|
||||
self.port
|
||||
)
|
||||
@ -100,9 +98,9 @@ class AgentPoolBackend(base.Backend):
|
||||
response, retry = self._make_and_send_dns_message(
|
||||
zone.name,
|
||||
self.timeout,
|
||||
pcodes.CC,
|
||||
pcodes.DELETE,
|
||||
pcodes.CLASSCC,
|
||||
private_codes.CC,
|
||||
private_codes.DELETE,
|
||||
private_codes.CLASSCC,
|
||||
self.host,
|
||||
self.port
|
||||
)
|
||||
@ -134,7 +132,7 @@ class AgentPoolBackend(base.Backend):
|
||||
'port': dest_port, 'timeout': timeout,
|
||||
'retry': retry})
|
||||
response = None
|
||||
elif isinstance(response, dns_query.BadResponse):
|
||||
elif isinstance(response, dns.query.BadResponse):
|
||||
LOG.warning("Got BadResponse while trying to send '%(msg)s' for "
|
||||
"'%(zone)s' to '%(server)s:%(port)d'. "
|
||||
"Timeout='%(timeout)d' seconds. Retry='%(retry)d'",
|
||||
@ -173,14 +171,10 @@ class AgentPoolBackend(base.Backend):
|
||||
|
||||
def _send_dns_message(self, dns_message, dest_ip, dest_port, timeout):
|
||||
try:
|
||||
if not CONF['service:mdns'].all_tcp:
|
||||
response = dns_query.udp(
|
||||
dns_message, dest_ip, port=dest_port, timeout=timeout)
|
||||
else:
|
||||
response = dns_query.tcp(
|
||||
dns_message, dest_ip, port=dest_port, timeout=timeout)
|
||||
return response
|
||||
return dnsutils.send_dns_message(
|
||||
dns_message, dest_ip, port=dest_port, timeout=timeout
|
||||
)
|
||||
except dns.exception.Timeout as timeout:
|
||||
return timeout
|
||||
except dns_query.BadResponse as badResponse:
|
||||
except dns.query.BadResponse as badResponse:
|
||||
return badResponse
|
||||
|
@ -20,7 +20,8 @@ import time
|
||||
|
||||
import dns
|
||||
import dns.exception
|
||||
from dns import rdatatype
|
||||
import dns.query
|
||||
import dns.rdatatype
|
||||
import dns.zone
|
||||
import eventlet
|
||||
from oslo_log import log as logging
|
||||
@ -312,7 +313,7 @@ def dnspyrecords_to_recordsetlist(dnspython_records):
|
||||
|
||||
|
||||
def dnspythonrecord_to_recordset(rname, rdataset):
|
||||
record_type = rdatatype.to_text(rdataset.rdtype)
|
||||
record_type = dns.rdatatype.to_text(rdataset.rdtype)
|
||||
|
||||
name = rname.to_text()
|
||||
if isinstance(name, bytes):
|
||||
@ -346,39 +347,122 @@ def do_axfr(zone_name, servers, timeout=None, source=None):
|
||||
timeout = timeout or CONF["service:mdns"].xfr_timeout
|
||||
|
||||
xfr = None
|
||||
|
||||
for srv in servers:
|
||||
to = eventlet.Timeout(timeout)
|
||||
log_info = {'name': zone_name, 'host': srv}
|
||||
try:
|
||||
LOG.info("Doing AXFR for %(name)s from %(host)s", log_info)
|
||||
|
||||
xfr = dns.query.xfr(srv['host'], zone_name, relativize=False,
|
||||
timeout=1, port=srv['port'], source=source)
|
||||
raw_zone = dns.zone.from_xfr(xfr, relativize=False)
|
||||
break
|
||||
except eventlet.Timeout as t:
|
||||
if t == to:
|
||||
LOG.error("AXFR timed out for %(name)s from %(host)s",
|
||||
log_info)
|
||||
continue
|
||||
except dns.exception.FormError:
|
||||
LOG.error("Zone %(name)s is not present on %(host)s."
|
||||
"Trying next server.", log_info)
|
||||
except socket.error:
|
||||
LOG.error("Connection error when doing AXFR for %(name)s from "
|
||||
"%(host)s", log_info)
|
||||
except Exception:
|
||||
LOG.exception("Problem doing AXFR %(name)s from %(host)s. "
|
||||
for address in get_ip_addresses(srv['host']):
|
||||
to = eventlet.Timeout(timeout)
|
||||
log_info = {'name': zone_name, 'host': srv, 'address': address}
|
||||
try:
|
||||
LOG.info(
|
||||
'Doing AXFR for %(name)s from %(host)s %(address)s',
|
||||
log_info
|
||||
)
|
||||
xfr = dns.query.xfr(
|
||||
address, zone_name, relativize=False, timeout=1,
|
||||
port=srv['port'], source=source
|
||||
)
|
||||
raw_zone = dns.zone.from_xfr(xfr, relativize=False)
|
||||
LOG.debug("AXFR Successful for %s", raw_zone.origin.to_text())
|
||||
return raw_zone
|
||||
except eventlet.Timeout as t:
|
||||
if t == to:
|
||||
LOG.error("AXFR timed out for %(name)s from %(host)s",
|
||||
log_info)
|
||||
continue
|
||||
except dns.exception.FormError:
|
||||
LOG.error("Zone %(name)s is not present on %(host)s."
|
||||
"Trying next server.", log_info)
|
||||
finally:
|
||||
to.cancel()
|
||||
continue
|
||||
else:
|
||||
raise exceptions.XFRFailure(
|
||||
"XFR failed for %(name)s. No servers in %(servers)s was reached." %
|
||||
{"name": zone_name, "servers": servers})
|
||||
except socket.error:
|
||||
LOG.error("Connection error when doing AXFR for %(name)s from "
|
||||
"%(host)s", log_info)
|
||||
except Exception:
|
||||
LOG.exception("Problem doing AXFR %(name)s from %(host)s. "
|
||||
"Trying next server.", log_info)
|
||||
finally:
|
||||
to.cancel()
|
||||
|
||||
LOG.debug("AXFR Successful for %s", raw_zone.origin.to_text())
|
||||
raise exceptions.XFRFailure(
|
||||
"XFR failed for %(name)s. No servers in %(servers)s was reached." %
|
||||
{"name": zone_name, "servers": servers}
|
||||
)
|
||||
|
||||
return raw_zone
|
||||
|
||||
def prepare_msg(zone_name, rdatatype=dns.rdatatype.SOA,
|
||||
dns_opcode=dns.opcode.QUERY):
|
||||
"""
|
||||
Do the needful to set up a dns packet with dnspython
|
||||
"""
|
||||
dns_message = dns.message.make_query(zone_name, rdatatype)
|
||||
dns_message.set_opcode(dns_opcode)
|
||||
|
||||
return dns_message
|
||||
|
||||
|
||||
def dig(zone_name, host, rdatatype, port=53):
|
||||
"""
|
||||
Set up and send a regular dns query, datatype configurable
|
||||
"""
|
||||
query = prepare_msg(zone_name, rdatatype=rdatatype)
|
||||
|
||||
return send_dns_message(query, host, port=port)
|
||||
|
||||
|
||||
def notify(zone_name, host, port=53):
|
||||
"""
|
||||
Set up a notify packet and send it
|
||||
"""
|
||||
msg = prepare_msg(zone_name, dns_opcode=dns.opcode.NOTIFY)
|
||||
|
||||
return send_dns_message(msg, host, port=port)
|
||||
|
||||
|
||||
def send_dns_message(dns_message, host, port=53, timeout=10):
|
||||
"""
|
||||
Send the dns message and return the response
|
||||
|
||||
:return: dns.Message of the response to the dns query
|
||||
"""
|
||||
ip_address = get_ip_address(host)
|
||||
# This can raise some exceptions, but we'll catch them elsewhere
|
||||
if not CONF['service:mdns'].all_tcp:
|
||||
return dns.query.udp(
|
||||
dns_message, ip_address, port=port, timeout=timeout)
|
||||
return dns.query.tcp(
|
||||
dns_message, ip_address, port=port, timeout=timeout)
|
||||
|
||||
|
||||
def get_serial(zone_name, host, port=53):
|
||||
"""
|
||||
Possibly raises dns.exception.Timeout or dns.query.BadResponse.
|
||||
Possibly returns 0 if, e.g., the answer section is empty.
|
||||
"""
|
||||
resp = dig(zone_name, host, dns.rdatatype.SOA, port=port)
|
||||
if not resp.answer:
|
||||
return 0
|
||||
rdataset = resp.answer[0].to_rdataset()
|
||||
if not rdataset:
|
||||
return 0
|
||||
return rdataset[0].serial
|
||||
|
||||
|
||||
def get_ip_address(ip_address_or_hostname):
|
||||
"""
|
||||
Provide an ip or hostname and return a valid ip4 or ipv6 address.
|
||||
|
||||
:return: ip address
|
||||
"""
|
||||
addresses = get_ip_addresses(ip_address_or_hostname)
|
||||
if not addresses:
|
||||
return None
|
||||
return addresses[0]
|
||||
|
||||
|
||||
def get_ip_addresses(ip_address_or_hostname):
|
||||
"""
|
||||
Provide an ip or hostname and return all valid ip4 or ipv6 addresses.
|
||||
|
||||
:return: ip addresses
|
||||
"""
|
||||
addresses = []
|
||||
for res in socket.getaddrinfo(ip_address_or_hostname, 0):
|
||||
addresses.append(res[4][0])
|
||||
return list(set(addresses))
|
||||
|
@ -28,6 +28,7 @@ import eventlet
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate import dnsutils
|
||||
from designate.mdns import base
|
||||
from designate.metrics import metrics
|
||||
|
||||
@ -186,8 +187,9 @@ class NotifyEndpoint(base.BaseEndpoint):
|
||||
'zone': zone.name, 'server': host,
|
||||
'port': port})
|
||||
try:
|
||||
response = self._send_dns_message(dns_message, host, port,
|
||||
timeout)
|
||||
response = dnsutils.send_dns_message(
|
||||
dns_message, host, port, timeout=timeout
|
||||
)
|
||||
|
||||
except socket.error as e:
|
||||
if e.errno != socket.errno.EAGAIN:
|
||||
@ -285,21 +287,3 @@ class NotifyEndpoint(base.BaseEndpoint):
|
||||
dns_message.flags |= dns.flags.RD
|
||||
|
||||
return dns_message
|
||||
|
||||
def _send_dns_message(self, dns_message, host, port, timeout):
|
||||
"""
|
||||
Send DNS Message over TCP or UDP, return response.
|
||||
|
||||
:param dns_message: The dns message that needs to be sent.
|
||||
:param host: The destination ip of dns_message.
|
||||
:param port: The destination port of dns_message.
|
||||
:param timeout: The timeout in seconds to wait for a response.
|
||||
:return: response
|
||||
"""
|
||||
send = dns_query.tcp if CONF['service:mdns'].all_tcp else dns_query.udp
|
||||
return send(
|
||||
dns_message,
|
||||
socket.gethostbyname(host),
|
||||
port=port,
|
||||
timeout=timeout
|
||||
)
|
||||
|
@ -14,11 +14,13 @@
|
||||
from unittest import mock
|
||||
|
||||
import dns
|
||||
import dns.query
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
|
||||
import designate.backend.agent as agent
|
||||
import designate.backend.private_codes as pcodes
|
||||
from designate import dnsutils
|
||||
from designate import exceptions
|
||||
from designate.mdns import rpcapi as mdns_api
|
||||
from designate import objects
|
||||
@ -130,7 +132,7 @@ class AgentBackendTestCase(tests.TestCase):
|
||||
def test_make_and_send_dns_message_bad_response(self):
|
||||
self.backend._make_dns_message = mock.Mock(return_value='')
|
||||
self.backend._send_dns_message = mock.Mock(
|
||||
return_value=agent.dns_query.BadResponse())
|
||||
return_value=dns.query.BadResponse())
|
||||
|
||||
out = self.backend._make_and_send_dns_message('h', 123, 1, 2, 3, 4, 5)
|
||||
|
||||
@ -176,50 +178,16 @@ class AgentBackendTestCase(tests.TestCase):
|
||||
|
||||
self.assertEqual((response, 0), out)
|
||||
|
||||
@mock.patch.object(agent.dns_query, 'tcp')
|
||||
@mock.patch.object(agent.dns_query, 'udp')
|
||||
def test_send_dns_message(self, mock_udp, mock_tcp):
|
||||
@mock.patch.object(dnsutils, 'get_ip_address')
|
||||
@mock.patch.object(dns.query, 'tcp')
|
||||
@mock.patch.object(dns.query, 'udp')
|
||||
def test_send_dns_message(self, mock_udp, mock_tcp, mock_get_ip_address):
|
||||
mock_udp.return_value = 'mock udp resp'
|
||||
mock_get_ip_address.return_value = '10.0.1.39'
|
||||
|
||||
out = self.backend._send_dns_message('msg', 'host', 123, 1)
|
||||
out = self.backend._send_dns_message('msg', '10.0.1.39', 123, 1)
|
||||
|
||||
self.assertFalse(agent.dns_query.tcp.called)
|
||||
agent.dns_query.udp.assert_called_with('msg', 'host', port=123,
|
||||
timeout=1)
|
||||
self.assertFalse(mock_tcp.called)
|
||||
mock_udp.assert_called_with('msg', '10.0.1.39', port=123,
|
||||
timeout=1)
|
||||
self.assertEqual('mock udp resp', out)
|
||||
|
||||
@mock.patch.object(agent.dns_query, 'tcp')
|
||||
@mock.patch.object(agent.dns_query, 'udp')
|
||||
def test_send_dns_message_timeout(self, mock_udp, mock_tcp):
|
||||
mock_udp.side_effect = dns.exception.Timeout
|
||||
|
||||
out = self.backend._send_dns_message('msg', 'host', 123, 1)
|
||||
|
||||
agent.dns_query.udp.assert_called_with('msg', 'host', port=123,
|
||||
timeout=1)
|
||||
self.assertIsInstance(out, dns.exception.Timeout)
|
||||
|
||||
@mock.patch.object(agent.dns_query, 'tcp')
|
||||
@mock.patch.object(agent.dns_query, 'udp')
|
||||
def test_send_dns_message_bad_response(self, mock_udp, mock_tcp):
|
||||
mock_udp.side_effect = agent.dns_query.BadResponse
|
||||
|
||||
out = self.backend._send_dns_message('msg', 'host', 123, 1)
|
||||
|
||||
agent.dns_query.udp.assert_called_with('msg', 'host', port=123,
|
||||
timeout=1)
|
||||
self.assertIsInstance(out, agent.dns_query.BadResponse)
|
||||
|
||||
@mock.patch.object(agent.dns_query, 'tcp')
|
||||
@mock.patch.object(agent.dns_query, 'udp')
|
||||
def test_send_dns_message_tcp(self, mock_udp, mock_tcp):
|
||||
self.CONF.set_override('all_tcp', True, 'service:mdns')
|
||||
|
||||
mock_tcp.return_value = 'mock tcp resp'
|
||||
|
||||
out = self.backend._send_dns_message('msg', 'host', 123, 1)
|
||||
|
||||
self.assertFalse(agent.dns_query.udp.called)
|
||||
agent.dns_query.tcp.assert_called_with('msg', 'host', port=123,
|
||||
timeout=1)
|
||||
self.assertEqual('mock tcp resp', out)
|
||||
|
@ -20,6 +20,7 @@ import dns
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
|
||||
from designate import dnsutils
|
||||
import designate.mdns.notify as notify
|
||||
import designate.tests
|
||||
from designate.tests.unit import RoObject
|
||||
@ -130,12 +131,11 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
self.assertEqual(('ERROR', 310, 0), out)
|
||||
|
||||
@mock.patch('time.sleep')
|
||||
def test_make_and_send_dns_message_timeout(self, mock_sleep):
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_timeout(self, mock_send_dns_message,
|
||||
mock_sleep):
|
||||
zone = RoObject(name='zn')
|
||||
self.notify._make_dns_message = mock.Mock(return_value='')
|
||||
self.notify._send_dns_message = mock.Mock(
|
||||
side_effect=dns.exception.Timeout
|
||||
)
|
||||
mock_send_dns_message.side_effect = dns.exception.Timeout
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
@ -143,12 +143,12 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
|
||||
self.assertEqual((None, 3), out)
|
||||
|
||||
def test_make_and_send_dns_message_bad_response(self):
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_bad_response(self,
|
||||
mock_send_dns_message):
|
||||
zone = RoObject(name='zn')
|
||||
self.notify._make_dns_message = mock.Mock(return_value='')
|
||||
self.notify._send_dns_message = mock.Mock(
|
||||
side_effect=notify.dns_query.BadResponse
|
||||
)
|
||||
mock_send_dns_message.side_effect = notify.dns_query.BadResponse
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
@ -157,15 +157,14 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
self.assertEqual((None, 1), out)
|
||||
|
||||
@mock.patch('time.sleep')
|
||||
def test_make_and_send_dns_message_eagain(self, mock_sleep):
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_eagain(self, mock_send_dns_message,
|
||||
mock_sleep):
|
||||
# bug #1558096
|
||||
zone = RoObject(name='zn')
|
||||
self.notify._make_dns_message = mock.Mock(return_value='')
|
||||
socket_error = socket.error()
|
||||
socket_error.errno = socket.errno.EAGAIN
|
||||
self.notify._send_dns_message = mock.Mock(
|
||||
side_effect=socket_error
|
||||
)
|
||||
mock_send_dns_message.side_effect = socket_error
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
@ -173,15 +172,15 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
|
||||
self.assertEqual((None, 3), out)
|
||||
|
||||
def test_make_and_send_dns_message_econnrefused(self):
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_econnrefused(self,
|
||||
mock_send_dns_message):
|
||||
# bug #1558096
|
||||
zone = RoObject(name='zn')
|
||||
self.notify._make_dns_message = mock.Mock(return_value='')
|
||||
socket_error = socket.error()
|
||||
socket_error.errno = socket.errno.ECONNREFUSED
|
||||
# socket errors other than EAGAIN should raise
|
||||
self.notify._send_dns_message = mock.Mock(
|
||||
side_effect=socket_error)
|
||||
mock_send_dns_message.side_effect = socket_error
|
||||
|
||||
self.assertRaises(
|
||||
socket.error,
|
||||
@ -189,11 +188,11 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
)
|
||||
|
||||
def test_make_and_send_dns_message_nxdomain(self):
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_nxdomain(self, mock_send_dns_message):
|
||||
zone = RoObject(name='zn')
|
||||
self.notify._make_dns_message = mock.Mock(return_value='')
|
||||
response = RoObject(rcode=mock.Mock(return_value=dns.rcode.NXDOMAIN))
|
||||
self.notify._send_dns_message = mock.Mock(return_value=response)
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
@ -201,17 +200,17 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
|
||||
self.assertEqual((response, 1), out)
|
||||
|
||||
def test_make_and_send_dns_message_missing_AA_flags(self):
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_missing_AA_flags(self,
|
||||
mock_send_dns_message):
|
||||
zone = RoObject(name='zn')
|
||||
self.notify._make_dns_message = mock.Mock(return_value='')
|
||||
|
||||
response = RoObject(
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
|
||||
# rcode is NOERROR but (flags & dns.flags.AA) gives 0
|
||||
flags=0,
|
||||
answer=['answer'],
|
||||
)
|
||||
self.notify._send_dns_message = mock.Mock(return_value=response)
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
@ -219,9 +218,10 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
|
||||
self.assertEqual((None, 1), out)
|
||||
|
||||
def test_make_and_send_dns_message_error_flags(self):
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_error_flags(self,
|
||||
mock_send_dns_message):
|
||||
zone = RoObject(name='zn')
|
||||
self.notify._make_dns_message = mock.Mock(return_value='')
|
||||
response = RoObject(
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
|
||||
# rcode is NOERROR but flags are not NOERROR
|
||||
@ -229,7 +229,7 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
ednsflags=321,
|
||||
answer=['answer'],
|
||||
)
|
||||
self.notify._send_dns_message = mock.Mock(return_value=response)
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
@ -266,23 +266,3 @@ class MdnsNotifyTest(designate.tests.TestCase):
|
||||
';AUTHORITY',
|
||||
';ADDITIONAL',
|
||||
], txt)
|
||||
|
||||
@mock.patch.object(notify.dns_query, 'udp')
|
||||
def test_send_udp_dns_message(self, mock_udp):
|
||||
self.CONF.set_override('all_tcp', False, 'service:mdns')
|
||||
|
||||
self.notify._send_dns_message('msg', '192.0.2.1', 1234, 1)
|
||||
|
||||
mock_udp.assert_called_with(
|
||||
'msg', '192.0.2.1', port=1234, timeout=1
|
||||
)
|
||||
|
||||
@mock.patch.object(notify.dns_query, 'tcp')
|
||||
def test_send_tcp_dns_message(self, mock_tcp):
|
||||
self.CONF.set_override('all_tcp', True, 'service:mdns')
|
||||
|
||||
self.notify._send_dns_message('msg', '192.0.2.1', 1234, 1)
|
||||
|
||||
mock_tcp.assert_called_with(
|
||||
'msg', '192.0.2.1', port=1234, timeout=1
|
||||
)
|
||||
|
@ -23,6 +23,7 @@ import dns.rcode
|
||||
import dns.rdatatype
|
||||
import dns.zone
|
||||
import eventlet
|
||||
from oslo_config import cfg
|
||||
import oslotest.base
|
||||
|
||||
from designate import dnsutils
|
||||
@ -30,6 +31,8 @@ from designate import exceptions
|
||||
from designate import objects
|
||||
import designate.tests
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
SAMPLES = {
|
||||
("cname.example.com.", "CNAME"): {
|
||||
"ttl": 10800,
|
||||
@ -319,3 +322,19 @@ class TestDoAfxr(oslotest.base.BaseTestCase):
|
||||
|
||||
self.assertTrue(mock_xfr.called)
|
||||
self.assertTrue(mock_from_xfr.called)
|
||||
|
||||
@mock.patch.object(dns.query, 'udp')
|
||||
def test_send_udp_dns_message(self, mock_udp):
|
||||
CONF.set_override('all_tcp', False, 'service:mdns')
|
||||
dnsutils.send_dns_message('msg', '192.0.2.1', 1234, 1)
|
||||
mock_udp.assert_called_with(
|
||||
'msg', '192.0.2.1', port=1234, timeout=1
|
||||
)
|
||||
|
||||
@mock.patch.object(dns.query, 'tcp')
|
||||
def test_send_tcp_dns_message(self, mock_tcp):
|
||||
CONF.set_override('all_tcp', True, 'service:mdns')
|
||||
dnsutils.send_dns_message('msg', '192.0.2.1', 1234, 1)
|
||||
mock_tcp.assert_called_with(
|
||||
'msg', '192.0.2.1', port=1234, timeout=1
|
||||
)
|
||||
|
@ -20,12 +20,12 @@ from oslo_config import cfg
|
||||
from oslo_config import fixture as cfg_fixture
|
||||
import oslotest.base
|
||||
|
||||
from designate import dnsutils
|
||||
from designate import exceptions
|
||||
from designate import objects
|
||||
from designate.tests.unit import utils
|
||||
from designate.worker import processing
|
||||
from designate.worker.tasks import zone
|
||||
from designate.worker import utils as wutils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -167,7 +167,7 @@ class TestZoneActionOnTarget(oslotest.base.BaseTestCase):
|
||||
self.context = mock.Mock()
|
||||
self.executor = mock.Mock()
|
||||
|
||||
@mock.patch.object(wutils, 'notify')
|
||||
@mock.patch.object(dnsutils, 'notify')
|
||||
def test_call_create(self, mock_notify):
|
||||
self.zone = objects.Zone(name='example.org.', action='CREATE')
|
||||
self.actor = zone.ZoneActionOnTarget(
|
||||
@ -185,7 +185,7 @@ class TestZoneActionOnTarget(oslotest.base.BaseTestCase):
|
||||
port=53
|
||||
)
|
||||
|
||||
@mock.patch.object(wutils, 'notify')
|
||||
@mock.patch.object(dnsutils, 'notify')
|
||||
def test_call_update(self, mock_notify):
|
||||
self.zone = objects.Zone(name='example.org.', action='UPDATE')
|
||||
self.actor = zone.ZoneActionOnTarget(
|
||||
@ -203,7 +203,7 @@ class TestZoneActionOnTarget(oslotest.base.BaseTestCase):
|
||||
port=53
|
||||
)
|
||||
|
||||
@mock.patch.object(wutils, 'notify')
|
||||
@mock.patch.object(dnsutils, 'notify')
|
||||
def test_call_delete(self, mock_notify):
|
||||
self.zone = objects.Zone(name='example.org.', action='DELETE')
|
||||
self.actor = zone.ZoneActionOnTarget(
|
||||
@ -217,7 +217,7 @@ class TestZoneActionOnTarget(oslotest.base.BaseTestCase):
|
||||
|
||||
mock_notify.assert_not_called()
|
||||
|
||||
@mock.patch.object(wutils, 'notify')
|
||||
@mock.patch.object(dnsutils, 'notify')
|
||||
@mock.patch('time.sleep', mock.Mock())
|
||||
def test_call_exception_raised(self, mock_notify):
|
||||
self.backend.create_zone.side_effect = exceptions.BadRequest()
|
||||
@ -250,7 +250,7 @@ class TestSendNotify(oslotest.base.BaseTestCase):
|
||||
|
||||
self.executor = mock.Mock()
|
||||
|
||||
@mock.patch.object(wutils, 'notify')
|
||||
@mock.patch.object(dnsutils, 'notify')
|
||||
def test_call_notify(self, mock_notify):
|
||||
self.zone = objects.Zone(name='example.org.')
|
||||
self.actor = zone.SendNotify(
|
||||
@ -267,7 +267,7 @@ class TestSendNotify(oslotest.base.BaseTestCase):
|
||||
port=53
|
||||
)
|
||||
|
||||
@mock.patch.object(wutils, 'notify')
|
||||
@mock.patch.object(dnsutils, 'notify')
|
||||
def test_call_notify_timeout(self, mock_notify):
|
||||
mock_notify.side_effect = dns.exception.Timeout()
|
||||
self.zone = objects.Zone(name='example.org.')
|
||||
@ -282,7 +282,7 @@ class TestSendNotify(oslotest.base.BaseTestCase):
|
||||
self.actor
|
||||
)
|
||||
|
||||
@mock.patch.object(wutils, 'notify')
|
||||
@mock.patch.object(dnsutils, 'notify')
|
||||
def test_call_dont_notify(self, mock_notify):
|
||||
CONF.set_override('notify', False, 'service:worker')
|
||||
|
||||
@ -668,11 +668,11 @@ class TestPollForZone(oslotest.base.BaseTestCase):
|
||||
self.task._max_retries = 3
|
||||
self.task._retry_interval = 2
|
||||
|
||||
@mock.patch.object(zone.wutils, 'get_serial', mock.Mock(return_value=10))
|
||||
@mock.patch.object(dnsutils, 'get_serial', mock.Mock(return_value=10))
|
||||
def test_get_serial(self):
|
||||
self.assertEqual(10, self.task._get_serial())
|
||||
|
||||
zone.wutils.get_serial.assert_called_with(
|
||||
dnsutils.get_serial.assert_called_with(
|
||||
'example.org.',
|
||||
'ns.example.org',
|
||||
port=53
|
||||
|
@ -29,7 +29,7 @@ class SendNotify(base.Task):
|
||||
port = int(self.target.options.get('port'))
|
||||
|
||||
try:
|
||||
wutils.notify(self.zone.name, host, port=port)
|
||||
dnsutils.notify(self.zone.name, host, port=port)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
@ -20,10 +20,10 @@ import dns
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate import dnsutils
|
||||
from designate import exceptions
|
||||
from designate import utils
|
||||
from designate.worker.tasks import base
|
||||
from designate.worker import utils as wutils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
@ -124,7 +124,7 @@ class SendNotify(base.Task):
|
||||
port = int(self.target.options.get('port'))
|
||||
|
||||
try:
|
||||
wutils.notify(self.zone.name, host, port=port)
|
||||
dnsutils.notify(self.zone.name, host, port=port)
|
||||
LOG.debug('Sent NOTIFY to %(host)s:%(port)s for zone %(zone)s',
|
||||
{
|
||||
'host': host,
|
||||
@ -311,7 +311,7 @@ class PollForZone(base.Task):
|
||||
self.ns = ns
|
||||
|
||||
def _get_serial(self):
|
||||
return wutils.get_serial(
|
||||
return dnsutils.get_serial(
|
||||
self.zone.name,
|
||||
self.ns.host,
|
||||
port=self.ns.port
|
||||
|
@ -1,82 +0,0 @@
|
||||
# Copyright 2016 Rackspace Inc.
|
||||
#
|
||||
# Author: Tim Simmons <tim.simmons@rackspace>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.mport threading
|
||||
import dns
|
||||
import dns.exception
|
||||
import dns.query
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def prepare_msg(zone_name, rdatatype=dns.rdatatype.SOA, notify=False):
|
||||
"""
|
||||
Do the needful to set up a dns packet with dnspython
|
||||
"""
|
||||
dns_message = dns.message.make_query(zone_name, rdatatype)
|
||||
if notify:
|
||||
dns_message.set_opcode(dns.opcode.NOTIFY)
|
||||
else:
|
||||
dns_message.set_opcode(dns.opcode.QUERY)
|
||||
return dns_message
|
||||
|
||||
|
||||
def dig(zone_name, host, rdatatype, port=53):
|
||||
"""
|
||||
Set up and send a regular dns query, datatype configurable
|
||||
"""
|
||||
query = prepare_msg(zone_name, rdatatype=rdatatype)
|
||||
|
||||
return send_dns_msg(query, host, port=port)
|
||||
|
||||
|
||||
def notify(zone_name, host, port=53):
|
||||
"""
|
||||
Set up a notify packet and send it
|
||||
"""
|
||||
msg = prepare_msg(zone_name, notify=True)
|
||||
|
||||
return send_dns_msg(msg, host, port=port)
|
||||
|
||||
|
||||
def send_dns_msg(dns_message, host, port=53):
|
||||
"""
|
||||
Send the dns message and return the response
|
||||
|
||||
:return: dns.Message of the response to the dns query
|
||||
"""
|
||||
# This can raise some exceptions, but we'll catch them elsewhere
|
||||
if not CONF['service:mdns'].all_tcp:
|
||||
return dns.query.udp(
|
||||
dns_message, host, port=port, timeout=10)
|
||||
else:
|
||||
return dns.query.tcp(
|
||||
dns_message, host, port=port, timeout=10)
|
||||
|
||||
|
||||
def get_serial(zone_name, host, port=53):
|
||||
"""
|
||||
Possibly raises dns.exception.Timeout or dns.query.BadResponse.
|
||||
Possibly returns 0 if, e.g., the answer section is empty.
|
||||
"""
|
||||
resp = dig(zone_name, host, dns.rdatatype.SOA, port=port)
|
||||
if not resp.answer:
|
||||
return 0
|
||||
rdataset = resp.answer[0].to_rdataset()
|
||||
if not rdataset:
|
||||
return 0
|
||||
return rdataset[0].serial
|
Loading…
Reference in New Issue
Block a user