Agent configuration and BIND9 improvements
* Make sure BIND9 rndc addzone/delzone compatiblity is maintained by stripping the trailing dot on zone names * Write out non-relativized zone files * Add a transfer-source option to the Agent for AXFRs * Add a query-destination option to the Agent BIND9 Backend * Remove the 1 second sleep, as the Pool Manager should be able to recover Change-Id: Ief7483d477affd17a13139f591dfe94be8285b74
This commit is contained in:
parent
1924d8e7e9
commit
eefeb1414e
@ -36,6 +36,8 @@ OPTS = [
|
||||
help='List of masters for the Agent, format ip:port'),
|
||||
cfg.StrOpt('backend-driver', default='bind9',
|
||||
help='The backend driver to use'),
|
||||
cfg.StrOpt('transfer-source', default=None,
|
||||
help='An IP address to be used to fetch zones transferred in'),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(OPTS, group='service:agent')
|
||||
|
@ -56,6 +56,7 @@ class RequestHandler(object):
|
||||
{'masters': self.masters})
|
||||
|
||||
self.allow_notify = CONF['service:agent'].allow_notify
|
||||
self.transfer_source = CONF['service:agent'].transfer_source
|
||||
backend_driver = cfg.CONF['service:agent'].backend_driver
|
||||
self.backend = agent_backend.get_backend(backend_driver, self)
|
||||
|
||||
@ -123,7 +124,8 @@ class RequestHandler(object):
|
||||
{'verb': "CREATE", 'name': domain_name, 'host': requester})
|
||||
|
||||
try:
|
||||
zone = dnsutils.do_axfr(domain_name, self.masters)
|
||||
zone = dnsutils.do_axfr(domain_name, self.masters,
|
||||
source=self.transfer_source)
|
||||
self.backend.create_domain(zone)
|
||||
except Exception:
|
||||
response.set_rcode(dns.rcode.from_text("SERVFAIL"))
|
||||
@ -173,7 +175,8 @@ class RequestHandler(object):
|
||||
# Check that the serial is < serial above
|
||||
|
||||
try:
|
||||
zone = dnsutils.do_axfr(domain_name, self.masters)
|
||||
zone = dnsutils.do_axfr(domain_name, self.masters,
|
||||
source=self.transfer_source)
|
||||
self.backend.update_domain(zone)
|
||||
except Exception:
|
||||
response.set_rcode(dns.rcode.from_text("SERVFAIL"))
|
||||
|
@ -13,10 +13,10 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import time
|
||||
import os
|
||||
|
||||
import dns
|
||||
import dns.resolver
|
||||
from oslo.config import cfg
|
||||
from oslo.concurrency import lockutils
|
||||
from oslo_log import log as logging
|
||||
@ -46,7 +46,9 @@ class Bind9Backend(base.AgentBackend):
|
||||
help='RNDC Config File'),
|
||||
cfg.StrOpt('rndc-key-file', default=None, help='RNDC Key File'),
|
||||
cfg.StrOpt('zone-file-path', default='$state_path/zones',
|
||||
help='Path where zone files are stored')
|
||||
help='Path where zone files are stored'),
|
||||
cfg.StrOpt('query-destination', default='127.0.0.1',
|
||||
help='Host to query when finding domains')
|
||||
]
|
||||
|
||||
return [(group, opts)]
|
||||
@ -57,7 +59,7 @@ class Bind9Backend(base.AgentBackend):
|
||||
def find_domain_serial(self, domain_name):
|
||||
LOG.debug("Finding %s" % domain_name)
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = ['127.0.0.1']
|
||||
resolver.nameservers = [cfg.CONF[CFG_GROUP].query_destination]
|
||||
try:
|
||||
rdata = resolver.query(domain_name, 'SOA')[0]
|
||||
except Exception:
|
||||
@ -77,7 +79,7 @@ class Bind9Backend(base.AgentBackend):
|
||||
|
||||
rndc_op = 'delzone'
|
||||
# RNDC doesn't like the trailing dot on the domain name
|
||||
rndc_call = self._rndc_base() + [rndc_op, domain_name[:-1]]
|
||||
rndc_call = self._rndc_base() + [rndc_op, domain_name.rstrip('.')]
|
||||
|
||||
utils.execute(*rndc_call)
|
||||
|
||||
@ -101,7 +103,9 @@ class Bind9Backend(base.AgentBackend):
|
||||
def _sync_domain(self, domain, new_domain_flag=False):
|
||||
"""Sync a single domain's zone file and reload bind config"""
|
||||
|
||||
domain_name = domain.origin.to_text()
|
||||
# NOTE: Different versions of BIND9 behave differently with a trailing
|
||||
# dot, so we're just going to take it off.
|
||||
domain_name = domain.origin.to_text().rstrip('.')
|
||||
|
||||
# NOTE: Only one thread should be working with the Zonefile at a given
|
||||
# time. The sleep(1) below introduces a not insignificant risk
|
||||
@ -112,9 +116,9 @@ class Bind9Backend(base.AgentBackend):
|
||||
zone_path = cfg.CONF[CFG_GROUP].zone_file_path
|
||||
|
||||
output_path = os.path.join(zone_path,
|
||||
'%szone' % domain_name)
|
||||
'%s.zone' % domain_name)
|
||||
|
||||
domain.to_file(output_path)
|
||||
domain.to_file(output_path, relativize=False)
|
||||
|
||||
rndc_call = self._rndc_base()
|
||||
|
||||
@ -130,13 +134,6 @@ class Bind9Backend(base.AgentBackend):
|
||||
rndc_call.extend([rndc_op])
|
||||
rndc_call.extend([domain_name])
|
||||
|
||||
if not new_domain_flag:
|
||||
# NOTE: Bind9 will only ever attempt to re-read a zonefile if
|
||||
# the file's timestamp has changed since the previous
|
||||
# reload. A one second sleep ensures we cross over a
|
||||
# second boundary before allowing the next change.
|
||||
time.sleep(1)
|
||||
|
||||
LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call))
|
||||
self._execute_rndc(rndc_call)
|
||||
|
||||
|
@ -184,7 +184,7 @@ def bind_udp(host, port):
|
||||
return sock_udp
|
||||
|
||||
|
||||
def do_axfr(zone_name, masters):
|
||||
def do_axfr(zone_name, masters, source=None):
|
||||
"""
|
||||
Performs an AXFR for a given zone name
|
||||
"""
|
||||
@ -195,7 +195,7 @@ def do_axfr(zone_name, masters):
|
||||
{'name': zone_name, 'host': master})
|
||||
|
||||
xfr = dns.query.xfr(master['ip'], zone_name, relativize=False,
|
||||
port=master['port'])
|
||||
port=master['port'], source=source)
|
||||
|
||||
try:
|
||||
# TODO(Tim): Add a timeout to this function
|
||||
|
@ -29,13 +29,14 @@ class AgentRequestHandlerTest(AgentTestCase):
|
||||
super(AgentRequestHandlerTest, self).setUp()
|
||||
self.config(allow_notify=["0.0.0.0"],
|
||||
backend_driver="fake",
|
||||
transfer_source="1.2.3.4",
|
||||
group='service:agent')
|
||||
self.handler = handler.RequestHandler()
|
||||
self.addr = ["0.0.0.0", 5558]
|
||||
|
||||
@mock.patch.object(dns.resolver.Resolver, 'query')
|
||||
@mock.patch('designate.dnsutils.do_axfr')
|
||||
def test_receive_notify(self, func, axfrfunc):
|
||||
def test_receive_notify(self, doaxfr, query):
|
||||
"""
|
||||
Get a NOTIFY and ensure the response is right,
|
||||
and an AXFR is triggered
|
||||
@ -83,7 +84,7 @@ class AgentRequestHandlerTest(AgentTestCase):
|
||||
|
||||
@mock.patch.object(dns.resolver.Resolver, 'query')
|
||||
@mock.patch('designate.dnsutils.do_axfr')
|
||||
def test_receive_create(self, func, func2):
|
||||
def test_receive_create(self, doaxfr, query):
|
||||
"""
|
||||
Get a CREATE and ensure the response is right,
|
||||
and an AXFR is triggered, and the proper backend
|
||||
@ -180,3 +181,31 @@ class AgentRequestHandlerTest(AgentTestCase):
|
||||
response = self.handler(request).to_wire()
|
||||
|
||||
self.assertEqual(expected_response, binascii.b2a_hex(response))
|
||||
|
||||
@mock.patch.object(dns.resolver.Resolver, 'query')
|
||||
@mock.patch.object(designate.dnsutils, 'do_axfr')
|
||||
def test_transfer_source(self, doaxfr, query):
|
||||
"""
|
||||
Get a CREATE and ensure the response is right,
|
||||
and an AXFR is triggered with the right source IP
|
||||
"""
|
||||
payload = "735d70000001000000000000076578616d706c6503636f6d00ff02ff00"
|
||||
# Expected NOERROR other fields are
|
||||
# opcode 14
|
||||
# rcode NOERROR
|
||||
# flags QR AA
|
||||
# ;QUESTION
|
||||
# example.com. CLASS65280 TYPE65282
|
||||
# ;ANSWER
|
||||
# ;AUTHORITY
|
||||
# ;ADDITIONAL
|
||||
expected_response = ("735df4000001000000000000076578616d706c6503636f6d"
|
||||
"00ff02ff00")
|
||||
request = dns.message.from_wire(binascii.a2b_hex(payload))
|
||||
request.environ = {'addr': ["0.0.0.0", 1234]}
|
||||
with mock.patch.object(
|
||||
designate.backend.agent_backend.impl_fake.FakeBackend,
|
||||
'find_domain_serial', return_value=None):
|
||||
response = self.handler(request).to_wire()
|
||||
doaxfr.assert_called_with('example.com.', [], source="1.2.3.4")
|
||||
self.assertEqual(expected_response, binascii.b2a_hex(response))
|
||||
|
@ -138,6 +138,7 @@ debug = False
|
||||
#allow_notify = 127.0.0.1
|
||||
#masters = 127.0.0.1:5354
|
||||
#backend_driver = fake
|
||||
#transfer_source = None
|
||||
|
||||
|
||||
#-----------------------
|
||||
@ -269,3 +270,4 @@ debug = False
|
||||
#rndc_config_file = /etc/rndc.conf
|
||||
#rndc_key_file = /etc/rndc.key
|
||||
#zone_file_path = $state_path/zones
|
||||
#query_destination = 127.0.0.1
|
||||
|
Loading…
Reference in New Issue
Block a user