Merge "Removed RPC calls from MDNS and moved them to the Worker"
This commit is contained in:
commit
d05232fc07
@ -185,7 +185,7 @@ class RequestHandler(object):
|
||||
|
||||
try:
|
||||
zone = dnsutils.do_axfr(zone_name, self.masters,
|
||||
source=self.transfer_source)
|
||||
source=self.transfer_source)
|
||||
self.backend.update_zone(zone)
|
||||
except Exception:
|
||||
response.set_rcode(dns.rcode.from_text("SERVFAIL"))
|
||||
|
@ -40,7 +40,7 @@ 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
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
@ -59,10 +59,6 @@ class AgentPoolBackend(base.Backend):
|
||||
self.max_retries = CONF['service:worker'].poll_max_retries
|
||||
# FIXME: the agent retries creating zones without any interval
|
||||
|
||||
@property
|
||||
def mdns_api(self):
|
||||
return mdns_api.MdnsAPI.get_instance()
|
||||
|
||||
def create_zone(self, context, zone):
|
||||
LOG.debug('Create Zone')
|
||||
response = self._make_and_send_dns_message(
|
||||
|
@ -19,7 +19,6 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate.context import DesignateContext
|
||||
from designate.mdns import rpcapi as mdns_api
|
||||
from designate.plugin import DriverPlugin
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -58,10 +57,6 @@ class Backend(DriverPlugin):
|
||||
def stop(self):
|
||||
LOG.info('Stopped %s backend', self.get_canonical_name())
|
||||
|
||||
@property
|
||||
def mdns_api(self):
|
||||
return mdns_api.MdnsAPI.get_instance()
|
||||
|
||||
# Core Backend Interface
|
||||
@abc.abstractmethod
|
||||
def create_zone(self, context, zone):
|
||||
|
@ -37,7 +37,6 @@ from designate import context as dcontext
|
||||
from designate import coordination
|
||||
from designate import dnsutils
|
||||
from designate import exceptions
|
||||
from designate.mdns import rpcapi as mdns_rpcapi
|
||||
from designate import network_api
|
||||
from designate import notifications
|
||||
from designate import objects
|
||||
@ -243,10 +242,6 @@ class Service(service.RPCService):
|
||||
self.coordination.stop()
|
||||
super(Service, self).stop(graceful)
|
||||
|
||||
@property
|
||||
def mdns_api(self):
|
||||
return mdns_rpcapi.MdnsAPI.get_instance()
|
||||
|
||||
@property
|
||||
def worker_api(self):
|
||||
return worker_rpcapi.WorkerAPI.get_instance()
|
||||
@ -957,7 +952,7 @@ class Service(service.RPCService):
|
||||
self.worker_api.create_zone(context, zone)
|
||||
|
||||
if zone.type == 'SECONDARY':
|
||||
self.mdns_api.perform_zone_xfr(context, zone)
|
||||
self.worker_api.perform_zone_xfr(context, zone)
|
||||
|
||||
# If zone is a superzone, update subzones
|
||||
# with new parent IDs
|
||||
@ -1116,7 +1111,7 @@ class Service(service.RPCService):
|
||||
|
||||
# Fire off a XFR
|
||||
if 'masters' in changes:
|
||||
self.mdns_api.perform_zone_xfr(context, zone)
|
||||
self.worker_api.perform_zone_xfr(context, zone)
|
||||
|
||||
self.worker_api.update_zone(context, zone)
|
||||
|
||||
@ -1239,15 +1234,15 @@ class Service(service.RPCService):
|
||||
# Ensure the format of the servers are correct, then poll the
|
||||
# serial
|
||||
srv = random.choice(zone.masters)
|
||||
status, serial, retries = self.mdns_api.get_serial_number(
|
||||
context, zone, srv.host, srv.port, 3, 1, 3, 0)
|
||||
status, serial = self.worker_api.get_serial_number(
|
||||
context, zone, srv.host, srv.port)
|
||||
|
||||
# Perform XFR if serial's are not equal
|
||||
if serial > zone.serial:
|
||||
if serial is not None and serial > zone.serial:
|
||||
LOG.info("Serial %(srv_serial)d is not equal to zone's "
|
||||
"%(serial)d, performing AXFR",
|
||||
{"srv_serial": serial, "serial": zone.serial})
|
||||
self.mdns_api.perform_zone_xfr(context, zone)
|
||||
self.worker_api.perform_zone_xfr(context, zone)
|
||||
|
||||
@rpc.expected_exceptions()
|
||||
def count_zones(self, context, criterion=None):
|
||||
|
@ -28,7 +28,6 @@ from designate.conf import infoblox
|
||||
from designate.conf import keystone
|
||||
from designate.conf import knot2
|
||||
from designate.conf import mdns
|
||||
from designate.conf import metrics
|
||||
from designate.conf import msdns
|
||||
from designate.conf import network_api
|
||||
from designate.conf import producer
|
||||
@ -54,7 +53,6 @@ infoblox.register_opts(CONF)
|
||||
keystone.register_opts(CONF)
|
||||
knot2.register_opts(CONF)
|
||||
mdns.register_opts(CONF)
|
||||
metrics.register_opts(CONF)
|
||||
msdns.register_opts(CONF)
|
||||
network_api.register_opts(CONF)
|
||||
producer.register_opts(CONF)
|
||||
|
@ -33,8 +33,12 @@ MDNS_OPTS = [
|
||||
help='mDNS TCP Backlog'),
|
||||
cfg.FloatOpt('tcp_recv_timeout', default=0.5,
|
||||
help='mDNS TCP Receive Timeout'),
|
||||
cfg.BoolOpt('all_tcp', default=False,
|
||||
help='Send all traffic over TCP'),
|
||||
cfg.IntOpt('all_tcp', help='Send all traffic over TCP',
|
||||
default=None,
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='This parameter should now be configured in'
|
||||
'service:worker instead',
|
||||
deprecated_since='Zed'),
|
||||
cfg.BoolOpt('query_enforce_tsig', default=False,
|
||||
help='Enforce all incoming queries (including AXFR) are TSIG '
|
||||
'signed'),
|
||||
@ -45,7 +49,11 @@ MDNS_OPTS = [
|
||||
cfg.StrOpt('topic', default='mdns',
|
||||
help='RPC topic name for mdns'),
|
||||
cfg.IntOpt('xfr_timeout', help="Timeout in seconds for XFR's.",
|
||||
default=10),
|
||||
default=None,
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='This parameter should now be configured in'
|
||||
'service:worker instead',
|
||||
deprecated_since='Zed'),
|
||||
]
|
||||
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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.
|
||||
from oslo_config import cfg
|
||||
|
||||
METRICS_GROUP = cfg.OptGroup(
|
||||
name='monasca:statsd',
|
||||
title="Configuration for Monasca Statsd"
|
||||
)
|
||||
|
||||
METRICS_OPTS = [
|
||||
cfg.BoolOpt('enabled', default=False, help='enable'),
|
||||
cfg.IntOpt('port', default=8125, help='UDP port'),
|
||||
cfg.StrOpt('hostname', default='127.0.0.1', help='hostname'),
|
||||
]
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_group(METRICS_GROUP)
|
||||
conf.register_opts(METRICS_OPTS, group=METRICS_GROUP)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return {
|
||||
METRICS_GROUP: METRICS_OPTS
|
||||
}
|
@ -46,6 +46,21 @@ WORKER_OPTS = [
|
||||
help='Whether to allow synchronous zone exports'),
|
||||
cfg.StrOpt('topic', default='worker',
|
||||
help='RPC topic name for worker'),
|
||||
cfg.IntOpt('xfr_timeout', help="Timeout in seconds for XFR's.",
|
||||
default=10),
|
||||
cfg.IntOpt('serial_max_retries',
|
||||
help='The maximum number of times to retry fetching a zones '
|
||||
'serial.',
|
||||
default=3),
|
||||
cfg.IntOpt('serial_retry_delay',
|
||||
help='The time to wait before retrying a zone serial request.',
|
||||
default=1),
|
||||
cfg.IntOpt('serial_timeout',
|
||||
help='Timeout in seconds before giving up on fetching a zones '
|
||||
'serial.',
|
||||
default=1),
|
||||
cfg.BoolOpt('all_tcp', default=False,
|
||||
help='Send all traffic over TCP'),
|
||||
]
|
||||
|
||||
|
||||
|
@ -337,19 +337,25 @@ def dnspythonrecord_to_recordset(rname, rdataset):
|
||||
return rrset
|
||||
|
||||
|
||||
def do_axfr(zone_name, servers, timeout=None, source=None):
|
||||
def xfr_timeout():
|
||||
if CONF['service:mdns'].xfr_timeout is not None:
|
||||
return CONF['service:mdns'].xfr_timeout
|
||||
else:
|
||||
return CONF['service:worker'].xfr_timeout
|
||||
|
||||
|
||||
def do_axfr(zone_name, servers, source=None):
|
||||
"""
|
||||
Requests an AXFR for a given zone name and process the response
|
||||
|
||||
:returns: Zone instance from dnspython
|
||||
"""
|
||||
random.shuffle(servers)
|
||||
timeout = timeout or CONF["service:mdns"].xfr_timeout
|
||||
|
||||
xfr = None
|
||||
for srv in servers:
|
||||
for address in get_ip_addresses(srv['host']):
|
||||
to = eventlet.Timeout(timeout)
|
||||
to = eventlet.Timeout(xfr_timeout())
|
||||
log_info = {'name': zone_name, 'host': srv, 'address': address}
|
||||
try:
|
||||
LOG.info(
|
||||
@ -415,6 +421,24 @@ def notify(zone_name, host, port=53):
|
||||
return send_dns_message(msg, host, port=port)
|
||||
|
||||
|
||||
def soa(zone_name, host, port=53, timeout=10):
|
||||
"""
|
||||
Set up a soa packet and send it
|
||||
"""
|
||||
msg = prepare_msg(zone_name, rdatatype=dns.rdatatype.SOA,
|
||||
dns_opcode=dns.opcode.QUERY)
|
||||
msg.flags |= dns.flags.RD
|
||||
|
||||
return send_dns_message(msg, host, port=port, timeout=timeout)
|
||||
|
||||
|
||||
def use_all_tcp():
|
||||
if CONF['service:mdns'].all_tcp is not None:
|
||||
return CONF['service:mdns'].all_tcp
|
||||
else:
|
||||
return CONF['service:worker'].all_tcp
|
||||
|
||||
|
||||
def send_dns_message(dns_message, host, port=53, timeout=10):
|
||||
"""
|
||||
Send the dns message and return the response
|
||||
@ -423,7 +447,7 @@ def send_dns_message(dns_message, host, port=53, timeout=10):
|
||||
"""
|
||||
ip_address = get_ip_address(host)
|
||||
# This can raise some exceptions, but we'll catch them elsewhere
|
||||
if not CONF['service:mdns'].all_tcp:
|
||||
if not use_all_tcp():
|
||||
return dns.query.udp(
|
||||
dns_message, ip_address, port=port, timeout=timeout)
|
||||
return dns.query.tcp(
|
||||
|
@ -1,38 +0,0 @@
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@hpe.com>
|
||||
#
|
||||
# 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.
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging as messaging
|
||||
|
||||
from designate.central import rpcapi as central_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseEndpoint(object):
|
||||
# Endpoints which extend this base must provide these properties
|
||||
RPC_API_NAMESPACE = None
|
||||
RPC_API_VERSION = None
|
||||
|
||||
def __init__(self, tg):
|
||||
LOG.info("Initialized mDNS %s endpoint", self.RPC_API_NAMESPACE)
|
||||
self.tg = tg
|
||||
self.target = messaging.Target(
|
||||
namespace=self.RPC_API_NAMESPACE,
|
||||
version=self.RPC_API_VERSION)
|
||||
|
||||
@property
|
||||
def central_api(self):
|
||||
return central_api.CentralAPI.get_instance()
|
@ -23,9 +23,9 @@ import dns.rdatatype
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate.central import rpcapi as central_api
|
||||
from designate import exceptions
|
||||
from designate.mdns import xfr
|
||||
from designate.worker import rpcapi as worker_api
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
@ -38,18 +38,18 @@ CONF.import_opt('default_pool_id', 'designate.central',
|
||||
TSIG_RRSIZE = 10 + 64 + 160 + 1
|
||||
|
||||
|
||||
class RequestHandler(xfr.XFRMixin):
|
||||
class RequestHandler(object):
|
||||
def __init__(self, storage, tg):
|
||||
self._central_api = None
|
||||
self._worker_api = None
|
||||
|
||||
self.storage = storage
|
||||
self.tg = tg
|
||||
|
||||
@property
|
||||
def central_api(self):
|
||||
if not self._central_api:
|
||||
self._central_api = central_api.CentralAPI.get_instance()
|
||||
return self._central_api
|
||||
def worker_api(self):
|
||||
if not self._worker_api:
|
||||
self._worker_api = worker_api.WorkerAPI.get_instance()
|
||||
return self._worker_api
|
||||
|
||||
def __call__(self, request):
|
||||
"""
|
||||
@ -169,8 +169,7 @@ class RequestHandler(xfr.XFRMixin):
|
||||
'master_addr': master_addr.to_data()
|
||||
}
|
||||
)
|
||||
self.tg.add_thread(self.zone_sync, context, zone,
|
||||
[master_addr])
|
||||
self.worker_api.perform_zone_xfr(context, zone, [master_addr])
|
||||
|
||||
response.flags |= dns.flags.AA
|
||||
|
||||
|
@ -1,255 +0,0 @@
|
||||
# Copyright (c) 2014 Rackspace Hosting
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import socket
|
||||
import time
|
||||
|
||||
import dns
|
||||
import dns.exception
|
||||
import dns.flags
|
||||
import dns.message
|
||||
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 import dnsutils
|
||||
from designate.mdns import base
|
||||
|
||||
dns_query = eventlet.import_patched('dns.query')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class NotifyEndpoint(base.BaseEndpoint):
|
||||
RPC_API_VERSION = '2.2'
|
||||
RPC_API_NAMESPACE = 'notify'
|
||||
|
||||
def get_serial_number(self, context, zone, host, port, timeout,
|
||||
retry_interval, max_retries, delay):
|
||||
"""
|
||||
Get zone serial number from a resolver using retries.
|
||||
|
||||
:param context: The user context.
|
||||
:param zone: The designate zone object. This contains the zone
|
||||
name. zone.serial = expected_serial
|
||||
:param host: A notify is sent to this host.
|
||||
:param port: A notify is sent to this port.
|
||||
:param timeout: The time (in seconds) to wait for a SOA response from
|
||||
nameserver.
|
||||
:param retry_interval: The time (in seconds) between retries.
|
||||
:param max_retries: The maximum number of retries mindns would do for
|
||||
an expected serial number. After this many retries, mindns returns
|
||||
an ERROR.
|
||||
:param delay: The time to wait before sending the first request.
|
||||
:return: a tuple of (status, actual_serial, retries)
|
||||
status is either "SUCCESS" or "ERROR".
|
||||
actual_serial is either the serial number returned in the SOA
|
||||
message from the nameserver or None.
|
||||
retries is the number of retries left.
|
||||
The return value is just used for testing and not by pool manager.
|
||||
The pool manager is informed of the status with update_status.
|
||||
"""
|
||||
actual_serial = None
|
||||
status = 'ERROR'
|
||||
retries_left = max_retries
|
||||
time.sleep(delay)
|
||||
while True:
|
||||
response, retry_cnt = self._make_and_send_dns_message(
|
||||
zone, host, port, timeout, retry_interval, retries_left)
|
||||
|
||||
if response and (response.rcode() in (dns.rcode.NXDOMAIN,
|
||||
dns.rcode.REFUSED,
|
||||
dns.rcode.SERVFAIL) or
|
||||
not bool(response.answer)):
|
||||
status = 'NO_ZONE'
|
||||
if zone.serial == 0 and zone.action in ('DELETE', 'NONE'):
|
||||
actual_serial = 0
|
||||
break # Zone not expected to exist
|
||||
|
||||
elif response and len(response.answer) == 1 \
|
||||
and str(response.answer[0].name) == str(zone.name) \
|
||||
and response.answer[0].rdclass == dns.rdataclass.IN \
|
||||
and response.answer[0].rdtype == dns.rdatatype.SOA:
|
||||
# parse the SOA response and get the serial number
|
||||
rrset = response.answer[0]
|
||||
actual_serial = list(rrset.to_rdataset().items)[0].serial
|
||||
|
||||
# TODO(vinod): Account for serial number wrap around. Unix
|
||||
# timestamps are used where Designate is primary, but secondary
|
||||
# zones use different values.
|
||||
if actual_serial is not None and actual_serial >= zone.serial:
|
||||
# Everything looks good at this point. Return SUCCESS.
|
||||
status = 'SUCCESS'
|
||||
break
|
||||
|
||||
retries_left -= retry_cnt
|
||||
msg = ("Got lower serial for '%(zone)s' to '%(host)s:"
|
||||
"%(port)s'. Expected:'%(es)d'. Got:'%(as)s'."
|
||||
"Retries left='%(retries)d'") % {
|
||||
'zone': zone.name, 'host': host, 'port': port,
|
||||
'es': zone.serial, 'as': actual_serial,
|
||||
'retries': retries_left}
|
||||
|
||||
if not retries_left:
|
||||
# return with error
|
||||
LOG.warning(msg)
|
||||
break
|
||||
|
||||
LOG.debug(msg)
|
||||
# retry again
|
||||
time.sleep(retry_interval)
|
||||
|
||||
# Return retries_left for testing purposes.
|
||||
return status, actual_serial, retries_left
|
||||
|
||||
def _make_and_send_dns_message(self, zone, host, port, timeout,
|
||||
retry_interval, max_retries, notify=False):
|
||||
"""
|
||||
Generate and send a DNS message over TCP or UDP using retries
|
||||
and return response.
|
||||
|
||||
:param zone: The designate zone object. This contains the zone
|
||||
name.
|
||||
:param host: The destination host for the dns message.
|
||||
:param port: The destination port for the dns message.
|
||||
:param timeout: The time (in seconds) to wait for a response from
|
||||
destination.
|
||||
:param retry_interval: The time (in seconds) between retries.
|
||||
:param max_retries: The maximum number of retries mindns would do for
|
||||
a response. After this many retries, the function returns.
|
||||
:param notify: If true, a notify message is constructed else a SOA
|
||||
message is constructed.
|
||||
:return: a tuple of (response, current_retry) where
|
||||
response is the response on success or None on failure.
|
||||
current_retry is the current retry number
|
||||
"""
|
||||
dns_message = self._make_dns_message(zone.name, notify=notify)
|
||||
|
||||
retry = 0
|
||||
response = None
|
||||
|
||||
while retry < max_retries:
|
||||
retry += 1
|
||||
LOG.info("Sending '%(msg)s' for '%(zone)s' to '%(server)s:"
|
||||
"%(port)d'.",
|
||||
{'msg': 'NOTIFY' if notify else 'SOA',
|
||||
'zone': zone.name, 'server': host,
|
||||
'port': port})
|
||||
try:
|
||||
response = dnsutils.send_dns_message(
|
||||
dns_message, host, port, timeout=timeout
|
||||
)
|
||||
|
||||
except socket.error as e:
|
||||
if e.errno != socket.errno.EAGAIN:
|
||||
raise # unknown error, let it traceback
|
||||
|
||||
# Initial workaround for bug #1558096
|
||||
LOG.info("Got EAGAIN while trying to send '%(msg)s' for "
|
||||
"'%(zone)s' to '%(server)s:%(port)d'. "
|
||||
"Timeout='%(timeout)d' seconds. Retry='%(retry)d'",
|
||||
{'msg': 'NOTIFY' if notify else 'SOA',
|
||||
'zone': zone.name, 'server': host,
|
||||
'port': port, 'timeout': timeout,
|
||||
'retry': retry})
|
||||
# retry sending the message
|
||||
time.sleep(retry_interval)
|
||||
continue
|
||||
|
||||
except dns.exception.Timeout:
|
||||
LOG.warning(
|
||||
"Got Timeout while trying to send '%(msg)s' for "
|
||||
"'%(zone)s' to '%(server)s:%(port)d'. "
|
||||
"Timeout='%(timeout)d' seconds. Retry='%(retry)d'",
|
||||
{'msg': 'NOTIFY' if notify else 'SOA',
|
||||
'zone': zone.name, 'server': host,
|
||||
'port': port, 'timeout': timeout,
|
||||
'retry': retry})
|
||||
# retry sending the message if we get a Timeout.
|
||||
time.sleep(retry_interval)
|
||||
continue
|
||||
|
||||
except 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'",
|
||||
{'msg': 'NOTIFY' if notify else 'SOA',
|
||||
'zone': zone.name, 'server': host,
|
||||
'port': port, 'timeout': timeout,
|
||||
'retry': retry})
|
||||
break # no retries after BadResponse
|
||||
|
||||
# either we have a good response or an error that we don't want to
|
||||
# recover by retrying
|
||||
break
|
||||
|
||||
if not response:
|
||||
return None, retry
|
||||
|
||||
# Check that we actually got a NOERROR in the rcode and and an
|
||||
# authoritative answer
|
||||
refused_statuses = (
|
||||
dns.rcode.NXDOMAIN, dns.rcode.REFUSED, dns.rcode.SERVFAIL
|
||||
)
|
||||
if (response.rcode() in refused_statuses or
|
||||
(response.rcode() == dns.rcode.NOERROR and
|
||||
not bool(response.answer))):
|
||||
if notify:
|
||||
LOG.info(
|
||||
'%(zone)s not found on %(server)s:%(port)d',
|
||||
{
|
||||
'zone': zone.name,
|
||||
'server': host,
|
||||
'port': port
|
||||
}
|
||||
)
|
||||
elif (not (response.flags & dns.flags.AA) or
|
||||
dns.rcode.from_flags(response.flags,
|
||||
response.ednsflags) != dns.rcode.NOERROR):
|
||||
LOG.warning("Failed to get expected response while trying to "
|
||||
"send '%(msg)s' for '%(zone)s' to '%(server)s:"
|
||||
"%(port)d'.\nResponse message:\n%(resp)s\n",
|
||||
{'msg': 'NOTIFY' if notify else 'SOA',
|
||||
'zone': zone.name, 'server': host,
|
||||
'port': port, 'resp': str(response)})
|
||||
response = None
|
||||
|
||||
return response, retry
|
||||
|
||||
def _make_dns_message(self, zone_name, notify=False):
|
||||
"""
|
||||
This constructs a SOA query or a dns NOTIFY message.
|
||||
:param zone_name: The zone name for which a SOA/NOTIFY needs to be
|
||||
sent.
|
||||
:param notify: If true, a notify message is constructed else a SOA
|
||||
message is constructed.
|
||||
:return: The constructed message.
|
||||
"""
|
||||
dns_message = dns.message.make_query(zone_name, dns.rdatatype.SOA)
|
||||
dns_message.flags = 0
|
||||
if notify:
|
||||
dns_message.set_opcode(dns.opcode.NOTIFY)
|
||||
dns_message.flags |= dns.flags.AA
|
||||
else:
|
||||
# Setting the flags to RD causes BIND9 to respond with a NXDOMAIN.
|
||||
dns_message.set_opcode(dns.opcode.QUERY)
|
||||
dns_message.flags |= dns.flags.RD
|
||||
|
||||
return dns_message
|
@ -1,104 +0,0 @@
|
||||
# Copyright (c) 2014 Rackspace Hosting
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging as messaging
|
||||
|
||||
from designate.common import profiler
|
||||
from designate.loggingutils import rpc_logging
|
||||
from designate import rpc
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MDNS_API = None
|
||||
|
||||
|
||||
def reset():
|
||||
global MDNS_API
|
||||
MDNS_API = None
|
||||
|
||||
|
||||
@profiler.trace_cls("rpc")
|
||||
@rpc_logging(LOG, 'mdns')
|
||||
class MdnsAPI(object):
|
||||
|
||||
"""
|
||||
Client side of the mdns RPC API.
|
||||
|
||||
Notify API version history:
|
||||
|
||||
1.0 - Added notify_zone_changed and poll_for_serial_number.
|
||||
1.1 - Added get_serial_number.
|
||||
2.0 - Changed method signatures.
|
||||
2.1 - Removed unused functions.
|
||||
2.2 - Changed get_serial_number signature to make upgrade safer.
|
||||
|
||||
XFR API version history:
|
||||
1.0 - Added perform_zone_xfr.
|
||||
"""
|
||||
RPC_NOTIFY_API_VERSION = '2.2'
|
||||
RPC_XFR_API_VERSION = '1.0'
|
||||
|
||||
def __init__(self, topic=None):
|
||||
self.topic = topic if topic else cfg.CONF['service:mdns'].topic
|
||||
|
||||
notify_target = messaging.Target(topic=self.topic,
|
||||
namespace='notify',
|
||||
version=self.RPC_NOTIFY_API_VERSION)
|
||||
self.notify_client = rpc.get_client(notify_target, version_cap='2.2')
|
||||
|
||||
xfr_target = messaging.Target(topic=self.topic,
|
||||
namespace='xfr',
|
||||
version=self.RPC_XFR_API_VERSION)
|
||||
self.xfr_client = rpc.get_client(xfr_target, version_cap='1.0')
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
"""
|
||||
The rpc.get_client() which is called upon the API object initialization
|
||||
will cause a assertion error if the designate.rpc.TRANSPORT isn't setup
|
||||
by rpc.init() before.
|
||||
|
||||
This fixes that by creating the rpcapi when demanded.
|
||||
"""
|
||||
global MDNS_API
|
||||
if not MDNS_API:
|
||||
MDNS_API = cls()
|
||||
return MDNS_API
|
||||
|
||||
def get_serial_number(self, context, zone, host, port, timeout,
|
||||
retry_interval, max_retries, delay):
|
||||
LOG.info(
|
||||
"get_serial_number: Calling mdns for zone '%(zone)s', serial "
|
||||
"%(serial)s' on nameserver '%(host)s:%(port)s'",
|
||||
{
|
||||
'zone': zone.name,
|
||||
'serial': zone.serial,
|
||||
'host': host,
|
||||
'port': port
|
||||
})
|
||||
cctxt = self.notify_client.prepare()
|
||||
return cctxt.call(
|
||||
context, 'get_serial_number', zone=zone,
|
||||
host=host, port=port, timeout=timeout,
|
||||
retry_interval=retry_interval, max_retries=max_retries,
|
||||
delay=delay
|
||||
)
|
||||
|
||||
def perform_zone_xfr(self, context, zone):
|
||||
LOG.info("perform_zone_xfr: Calling mdns for zone %(zone)s",
|
||||
{"zone": zone.name})
|
||||
return self.xfr_client.cast(context, 'perform_zone_xfr', zone=zone)
|
@ -19,8 +19,6 @@ from oslo_log import log as logging
|
||||
from designate.conf.mdns import DEFAULT_MDNS_PORT
|
||||
from designate import dnsutils
|
||||
from designate.mdns import handler
|
||||
from designate.mdns import notify
|
||||
from designate.mdns import xfr
|
||||
from designate import service
|
||||
from designate import storage
|
||||
from designate import utils
|
||||
@ -29,20 +27,15 @@ LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class Service(service.RPCService):
|
||||
class Service(service.Service):
|
||||
_dns_default_port = DEFAULT_MDNS_PORT
|
||||
|
||||
def __init__(self):
|
||||
self._storage = None
|
||||
|
||||
super(Service, self).__init__(
|
||||
self.service_name, cfg.CONF['service:mdns'].topic,
|
||||
threads=cfg.CONF['service:mdns'].threads,
|
||||
self.service_name, threads=cfg.CONF['service:mdns'].threads,
|
||||
)
|
||||
self.override_endpoints(
|
||||
[notify.NotifyEndpoint(self.tg), xfr.XfrEndpoint(self.tg)]
|
||||
)
|
||||
|
||||
self.dns_service = service.DNSService(
|
||||
self.dns_application, self.tg,
|
||||
cfg.CONF['service:mdns'].listen,
|
||||
|
@ -1,64 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hpe.com>
|
||||
#
|
||||
# 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.
|
||||
import time
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from designate import dnsutils
|
||||
from designate import exceptions
|
||||
from designate.mdns import base
|
||||
from designate.metrics import metrics
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XFRMixin(object):
|
||||
"""
|
||||
Utility mixin that holds common methods for XFR functionality.
|
||||
"""
|
||||
def zone_sync(self, context, zone, servers=None):
|
||||
start_time = time.time()
|
||||
try:
|
||||
servers = servers or zone.masters
|
||||
servers = servers.to_list()
|
||||
|
||||
timeout = cfg.CONF["service:mdns"].xfr_timeout
|
||||
try:
|
||||
dnspython_zone = dnsutils.do_axfr(zone.name, servers,
|
||||
timeout=timeout)
|
||||
except exceptions.XFRFailure as e:
|
||||
LOG.warning(e)
|
||||
return
|
||||
|
||||
zone.update(dnsutils.from_dnspython_zone(dnspython_zone))
|
||||
|
||||
zone.transferred_at = timeutils.utcnow()
|
||||
|
||||
zone.obj_reset_changes(["name"])
|
||||
self.central_api.update_zone(context, zone, increment_serial=False)
|
||||
finally:
|
||||
metrics.timing('mdns.xfr.zone_sync', time.time() - start_time)
|
||||
|
||||
|
||||
class XfrEndpoint(base.BaseEndpoint, XFRMixin):
|
||||
RPC_API_VERSION = '1.0'
|
||||
RPC_API_NAMESPACE = 'xfr'
|
||||
|
||||
def perform_zone_xfr(self, context, zone):
|
||||
self.zone_sync(context, zone)
|
@ -1,80 +0,0 @@
|
||||
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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.
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
import designate.conf
|
||||
from designate.metrics_client import noop
|
||||
|
||||
monascastatsd = importutils.try_import('monascastatsd')
|
||||
|
||||
CFG_GROUP_NAME = 'monasca:statsd'
|
||||
CONF = designate.conf.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# Global metrics client to be imported by other modules
|
||||
metrics = None
|
||||
|
||||
|
||||
class Metrics(object):
|
||||
def __init__(self):
|
||||
self._client = None
|
||||
|
||||
def init(self):
|
||||
conf = cfg.CONF[CFG_GROUP_NAME]
|
||||
if conf.enabled and monascastatsd:
|
||||
LOG.info(
|
||||
'Statsd reports to %(host)s:%(port)d',
|
||||
{
|
||||
'host': conf.hostname,
|
||||
'port': conf.port
|
||||
}
|
||||
)
|
||||
self._client = monascastatsd.Client(
|
||||
host=conf.hostname, port=conf.port,
|
||||
dimensions={
|
||||
'service_name': 'dns'
|
||||
})
|
||||
return
|
||||
|
||||
if conf.enabled and not monascastatsd:
|
||||
LOG.error('monasca-statsd client not installed. '
|
||||
'Metrics will be ignored.')
|
||||
else:
|
||||
LOG.info('Statsd disabled')
|
||||
|
||||
self._client = noop.Client()
|
||||
|
||||
def counter(self, *a, **kw):
|
||||
return self.client.get_counter(*a, **kw)
|
||||
|
||||
def gauge(self, *a, **kw):
|
||||
return self.client.get_gauge(*a, **kw)
|
||||
|
||||
@property
|
||||
def timing(self):
|
||||
return self.client.get_timer().timing
|
||||
|
||||
def timer(self):
|
||||
return self.client.get_timer()
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
if not self._client:
|
||||
self.init()
|
||||
return self._client
|
||||
|
||||
|
||||
metrics = Metrics()
|
@ -1,85 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2016 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NoopConnection(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def _flush_buffer(self):
|
||||
pass
|
||||
|
||||
def close_buffer(self):
|
||||
pass
|
||||
|
||||
def connect(self, *a, **kw):
|
||||
pass
|
||||
|
||||
def open_buffer(self, *a, **kw):
|
||||
pass
|
||||
|
||||
|
||||
class NoopCounter(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def increment(self, *a, **kw):
|
||||
pass
|
||||
|
||||
def decrement(self, *a, **kw):
|
||||
pass
|
||||
|
||||
def __add__(self, value):
|
||||
pass
|
||||
|
||||
def __sub__(self, value):
|
||||
pass
|
||||
|
||||
|
||||
class NoopGauge(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def send(self, *a, **kw):
|
||||
pass
|
||||
|
||||
|
||||
class NoopTimer(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def timing(self, *a, **kw):
|
||||
pass
|
||||
|
||||
|
||||
class Client(object):
|
||||
def __init__(self, *a, **kw):
|
||||
self._counter = NoopCounter()
|
||||
self._gauge = NoopGauge()
|
||||
self._timer = NoopTimer()
|
||||
self.connection = NoopConnection()
|
||||
|
||||
def get_counter(self, *a, **kw):
|
||||
return self._counter
|
||||
|
||||
def get_gauge(self, *a, **kw):
|
||||
return self._gauge
|
||||
|
||||
def get_timer(self):
|
||||
return self._timer
|
@ -33,7 +33,6 @@ from oslo_utils import netutils
|
||||
from designate.common import profiler
|
||||
import designate.conf
|
||||
from designate.i18n import _
|
||||
from designate.metrics import metrics
|
||||
from designate import policy
|
||||
from designate import rpc
|
||||
from designate import utils
|
||||
@ -150,7 +149,6 @@ class DNSService(object):
|
||||
self.tcp_backlog = tcp_backlog
|
||||
self.tcp_recv_timeout = tcp_recv_timeout
|
||||
self.listen = listen
|
||||
metrics.init()
|
||||
|
||||
# Eventet will complain loudly about our use of multiple greentheads
|
||||
# reading/writing to the UDP socket at once. Disable this warning.
|
||||
|
@ -21,9 +21,9 @@ import oslo_messaging as messaging
|
||||
|
||||
from designate.central import service as central_service
|
||||
from designate import exceptions
|
||||
from designate.mdns import rpcapi as mdns_api
|
||||
from designate import objects
|
||||
from designate.tests.test_api.test_v2 import ApiV2TestCase
|
||||
from designate.worker import rpcapi as worker_api
|
||||
|
||||
|
||||
class ApiV2ZonesTest(ApiV2TestCase):
|
||||
@ -565,16 +565,17 @@ class ApiV2ZonesTest(ApiV2TestCase):
|
||||
# Create a zone
|
||||
zone = self.create_zone(**fixture)
|
||||
|
||||
mdns = mock.Mock()
|
||||
with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
|
||||
get_mdns.return_value = mdns
|
||||
mdns.get_serial_number.return_value = ('SUCCESS', 10, 1, )
|
||||
worker = mock.Mock()
|
||||
with mock.patch.object(worker_api.WorkerAPI,
|
||||
'get_instance') as get_worker:
|
||||
get_worker.return_value = worker
|
||||
worker.get_serial_number.return_value = ('SUCCESS', 10)
|
||||
|
||||
response = self.client.post_json(
|
||||
'/zones/%s/tasks/xfr' % zone['id'],
|
||||
None, status=202)
|
||||
|
||||
self.assertTrue(mdns.perform_zone_xfr.called)
|
||||
self.assertTrue(worker.perform_zone_xfr.called)
|
||||
|
||||
# Check the headers are what we expect
|
||||
self.assertEqual(202, response.status_int)
|
||||
|
@ -34,12 +34,12 @@ import testtools
|
||||
from testtools.matchers import GreaterThan
|
||||
|
||||
from designate import exceptions
|
||||
from designate.mdns import rpcapi as mdns_api
|
||||
from designate import objects
|
||||
from designate.storage.impl_sqlalchemy import tables
|
||||
from designate.tests import fixtures
|
||||
from designate.tests.test_central import CentralTestCase
|
||||
from designate import utils
|
||||
from designate.worker import rpcapi as worker_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -1307,13 +1307,15 @@ class CentralServiceTest(CentralTestCase):
|
||||
# Create a zone
|
||||
secondary = self.create_zone(**fixture)
|
||||
|
||||
mdns = mock.Mock()
|
||||
with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
|
||||
get_mdns.return_value = mdns
|
||||
mdns.get_serial_number.return_value = ('SUCCESS', 10, 1, )
|
||||
worker = mock.Mock()
|
||||
with mock.patch.object(worker_api.WorkerAPI,
|
||||
'get_instance') as get_worker:
|
||||
get_worker.return_value = worker
|
||||
worker.get_serial_number.return_value = ('SUCCESS', 10)
|
||||
|
||||
self.central_service.xfr_zone(self.admin_context, secondary.id)
|
||||
|
||||
self.assertTrue(mdns.perform_zone_xfr.called)
|
||||
self.assertTrue(worker.perform_zone_xfr.called)
|
||||
|
||||
def test_xfr_zone_same_serial(self):
|
||||
# Create a zone
|
||||
@ -1324,13 +1326,14 @@ class CentralServiceTest(CentralTestCase):
|
||||
# Create a zone
|
||||
secondary = self.create_zone(**fixture)
|
||||
|
||||
mdns = mock.Mock()
|
||||
with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
|
||||
get_mdns.return_value = mdns
|
||||
mdns.get_serial_number.return_value = ('SUCCESS', 1, 1, )
|
||||
worker = mock.Mock()
|
||||
with mock.patch.object(worker_api.WorkerAPI,
|
||||
'get_instance') as get_worker:
|
||||
get_worker.return_value = worker
|
||||
worker.get_serial_number.return_value = ('SUCCESS', 1)
|
||||
self.central_service.xfr_zone(self.admin_context, secondary.id)
|
||||
|
||||
self.assertFalse(mdns.perform_zone_xfr.called)
|
||||
self.assertFalse(worker.perform_zone_xfr.called)
|
||||
|
||||
def test_xfr_zone_lower_serial(self):
|
||||
# Create a zone
|
||||
@ -1343,13 +1346,14 @@ class CentralServiceTest(CentralTestCase):
|
||||
secondary = self.create_zone(**fixture)
|
||||
secondary.serial
|
||||
|
||||
mdns = mock.Mock()
|
||||
with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
|
||||
get_mdns.return_value = mdns
|
||||
mdns.get_serial_number.return_value = ('SUCCESS', 0, 1, )
|
||||
worker = mock.Mock()
|
||||
with mock.patch.object(worker_api.WorkerAPI,
|
||||
'get_instance') as get_worker:
|
||||
get_worker.return_value = worker
|
||||
worker.get_serial_number.return_value = ('SUCCESS', 0)
|
||||
self.central_service.xfr_zone(self.admin_context, secondary.id)
|
||||
|
||||
self.assertFalse(mdns.perform_zone_xfr.called)
|
||||
self.assertFalse(worker.perform_zone_xfr.called)
|
||||
|
||||
def test_xfr_zone_invalid_type(self):
|
||||
zone = self.create_zone()
|
||||
|
@ -177,9 +177,6 @@ class MdnsRequestHandlerTest(MdnsTestCase):
|
||||
return_value=zone):
|
||||
response = next(self.handler(request)).to_wire()
|
||||
|
||||
self.mock_tg.add_thread.assert_called_with(
|
||||
self.handler.zone_sync, self.context, zone,
|
||||
[zone.masters[0]])
|
||||
self.assertEqual(expected_response, binascii.b2a_hex(response))
|
||||
|
||||
@mock.patch.object(dns.resolver.Resolver, 'query')
|
||||
|
@ -21,13 +21,12 @@ import dns.exception
|
||||
import dns.message
|
||||
import dns.query
|
||||
|
||||
from designate.mdns import notify
|
||||
from designate import objects
|
||||
from designate.tests.test_mdns import MdnsTestCase
|
||||
from designate.tests import TestCase
|
||||
from designate.worker.tasks import zone
|
||||
|
||||
|
||||
class MdnsNotifyTest(MdnsTestCase):
|
||||
|
||||
class WorkerNotifyTest(TestCase):
|
||||
test_zone = {
|
||||
'name': 'example.com.',
|
||||
'email': 'example@example.com',
|
||||
@ -35,14 +34,13 @@ class MdnsNotifyTest(MdnsTestCase):
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(MdnsNotifyTest, self).setUp()
|
||||
super(WorkerNotifyTest, self).setUp()
|
||||
self.nameserver = objects.PoolNameserver.from_dict({
|
||||
'id': 'f278782a-07dc-4502-9177-b5d85c5f7c7e',
|
||||
'host': '127.0.0.1',
|
||||
'port': 65255
|
||||
})
|
||||
self.mock_tg = mock.Mock()
|
||||
self.notify = notify.NotifyEndpoint(self.mock_tg)
|
||||
|
||||
def test_poll_for_serial_number(self):
|
||||
# id 10001
|
||||
@ -62,12 +60,14 @@ class MdnsNotifyTest(MdnsTestCase):
|
||||
"00000e10")
|
||||
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
||||
binascii.a2b_hex(poll_response))):
|
||||
status, serial, retries = self.notify.get_serial_number(
|
||||
'context', objects.Zone.from_dict(self.test_zone),
|
||||
self.nameserver.host, self.nameserver.port, 0, 0, 2, 0)
|
||||
self.assertEqual(status, 'SUCCESS')
|
||||
self.assertEqual(serial, self.test_zone['serial'])
|
||||
self.assertEqual(retries, 2)
|
||||
get_zone_serial = zone.GetZoneSerial(
|
||||
self.mock_tg, 'context',
|
||||
objects.Zone.from_dict(self.test_zone),
|
||||
self.nameserver.host, self.nameserver.port,
|
||||
)
|
||||
result = get_zone_serial()
|
||||
self.assertEqual(result[0], 'SUCCESS')
|
||||
self.assertEqual(result[1], self.test_zone['serial'])
|
||||
|
||||
def test_poll_for_serial_number_lower_serial(self):
|
||||
# id 10001
|
||||
@ -87,12 +87,14 @@ class MdnsNotifyTest(MdnsTestCase):
|
||||
"00000e10")
|
||||
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
||||
binascii.a2b_hex(poll_response))):
|
||||
status, serial, retries = self.notify.get_serial_number(
|
||||
'context', objects.Zone.from_dict(self.test_zone),
|
||||
self.nameserver.host, self.nameserver.port, 0, 0, 2, 0)
|
||||
self.assertEqual(status, 'ERROR')
|
||||
self.assertEqual(serial, 99)
|
||||
self.assertEqual(retries, 0)
|
||||
get_zone_serial = zone.GetZoneSerial(
|
||||
self.mock_tg, 'context',
|
||||
objects.Zone.from_dict(self.test_zone),
|
||||
self.nameserver.host, self.nameserver.port,
|
||||
)
|
||||
result = get_zone_serial()
|
||||
self.assertEqual(result[0], 'SUCCESS')
|
||||
self.assertEqual(result[1], 99)
|
||||
|
||||
def test_poll_for_serial_number_higher_serial(self):
|
||||
# id 10001
|
||||
@ -112,18 +114,23 @@ class MdnsNotifyTest(MdnsTestCase):
|
||||
"00000e10")
|
||||
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
||||
binascii.a2b_hex(poll_response))):
|
||||
status, serial, retries = self.notify.get_serial_number(
|
||||
'context', objects.Zone.from_dict(self.test_zone),
|
||||
self.nameserver.host, self.nameserver.port, 0, 0, 2, 0)
|
||||
self.assertEqual(status, 'SUCCESS')
|
||||
self.assertEqual(serial, 101)
|
||||
self.assertEqual(retries, 2)
|
||||
get_zone_serial = zone.GetZoneSerial(
|
||||
self.mock_tg, 'context',
|
||||
objects.Zone.from_dict(self.test_zone),
|
||||
self.nameserver.host, self.nameserver.port,
|
||||
)
|
||||
result = get_zone_serial()
|
||||
self.assertEqual(result[0], 'SUCCESS')
|
||||
self.assertEqual(result[1], 101)
|
||||
|
||||
@patch.object(dns.query, 'udp', side_effect=dns.exception.Timeout)
|
||||
def test_poll_for_serial_number_timeout(self, _):
|
||||
status, serial, retries = self.notify.get_serial_number(
|
||||
'context', objects.Zone.from_dict(self.test_zone),
|
||||
self.nameserver.host, self.nameserver.port, 0, 0, 2, 0)
|
||||
self.assertEqual(status, 'ERROR')
|
||||
self.assertIsNone(serial)
|
||||
self.assertEqual(retries, 0)
|
||||
self.CONF.set_override('serial_timeout', 1, 'service:worker')
|
||||
get_zone_serial = zone.GetZoneSerial(
|
||||
self.mock_tg, 'context',
|
||||
objects.Zone.from_dict(self.test_zone),
|
||||
self.nameserver.host, self.nameserver.port,
|
||||
)
|
||||
result = get_zone_serial()
|
||||
self.assertEqual(result[0], 'ERROR')
|
||||
self.assertIsNone(result[1])
|
@ -24,6 +24,7 @@ from designate import exceptions
|
||||
from designate.mdns import handler
|
||||
from designate import objects
|
||||
from designate.tests import fixtures
|
||||
from designate.worker import rpcapi as worker_rpcapi
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -39,6 +40,14 @@ class MdnsHandleTest(oslotest.base.BaseTestCase):
|
||||
self.tg = mock.Mock()
|
||||
self.handler = handler.RequestHandler(self.storage, self.tg)
|
||||
|
||||
def test_worker_api(self):
|
||||
self.assertIsNone(self.handler._worker_api)
|
||||
self.assertIsInstance(self.handler.worker_api,
|
||||
worker_rpcapi.WorkerAPI)
|
||||
self.assertIsNotNone(self.handler._worker_api)
|
||||
self.assertIsInstance(self.handler.worker_api,
|
||||
worker_rpcapi.WorkerAPI)
|
||||
|
||||
@mock.patch.object(dns.resolver.Resolver, 'query')
|
||||
def test_notify(self, mock_query):
|
||||
self.storage.find_zone.return_value = objects.Zone(
|
||||
@ -206,7 +215,6 @@ class TestRequestHandlerCall(oslotest.base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestRequestHandlerCall, self).setUp()
|
||||
self.handler = handler.RequestHandler(mock.Mock(), mock.Mock())
|
||||
self.handler._central_api = mock.Mock(name='central_api')
|
||||
|
||||
# Use a simple handlers that doesn't require a real request
|
||||
self.handler._handle_query_error = mock.Mock(return_value='Error')
|
||||
@ -215,10 +223,6 @@ class TestRequestHandlerCall(oslotest.base.BaseTestCase):
|
||||
return_value=['Record Query'])
|
||||
self.handler._handle_notify = mock.Mock(return_value=['Notify'])
|
||||
|
||||
def test_central_api_property(self):
|
||||
self.handler._central_api = 'foo'
|
||||
self.assertEqual(self.handler.central_api, 'foo')
|
||||
|
||||
def test__call___unhandled_opcodes(self):
|
||||
unhandled_codes = [
|
||||
dns.opcode.STATUS,
|
||||
|
@ -1,258 +0,0 @@
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Federico Ceratto <federico.ceratto@hpe.com>
|
||||
#
|
||||
# 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.
|
||||
import socket
|
||||
from unittest import mock
|
||||
|
||||
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
|
||||
|
||||
|
||||
class MdnsNotifyTest(designate.tests.TestCase):
|
||||
def setUp(self):
|
||||
super(MdnsNotifyTest, self).setUp()
|
||||
|
||||
self.notify = notify.NotifyEndpoint(mock.Mock())
|
||||
|
||||
@mock.patch('time.sleep')
|
||||
def test_get_serial_number_nxdomain(self, mock_sleep):
|
||||
# The zone is not found but it was supposed to be there
|
||||
response = RoObject(
|
||||
answer=[RoObject(
|
||||
rdclass=dns.rdataclass.IN,
|
||||
rdtype=dns.rdatatype.SOA
|
||||
)],
|
||||
rcode=mock.Mock(return_value=dns.rcode.NXDOMAIN)
|
||||
)
|
||||
zone = RoObject(name='zn', serial=314)
|
||||
self.notify._make_and_send_dns_message = mock.Mock(
|
||||
return_value=(response, 1)
|
||||
)
|
||||
|
||||
out = self.notify.get_serial_number(
|
||||
'context', zone, 'h', 1234, 1, 2, 3, 4
|
||||
)
|
||||
|
||||
self.assertEqual(('NO_ZONE', None, 0), out)
|
||||
|
||||
@mock.patch('time.sleep')
|
||||
def test_get_serial_number_nxdomain_deleted_zone(self, mock_sleep):
|
||||
# The zone is not found and it's not was supposed be there
|
||||
response = RoObject(
|
||||
answer=[RoObject(
|
||||
rdclass=dns.rdataclass.IN,
|
||||
rdtype=dns.rdatatype.SOA
|
||||
)],
|
||||
rcode=mock.Mock(return_value=dns.rcode.NXDOMAIN)
|
||||
)
|
||||
zone = RoObject(name='zn', serial=0, action='DELETE')
|
||||
self.notify._make_and_send_dns_message = mock.Mock(
|
||||
return_value=(response, 1)
|
||||
)
|
||||
|
||||
out = self.notify.get_serial_number(
|
||||
'context', zone, 'h', 1234, 1, 2, 3, 4
|
||||
)
|
||||
|
||||
self.assertEqual(('NO_ZONE', 0, 3), out)
|
||||
|
||||
@mock.patch('time.sleep')
|
||||
def test_get_serial_number_ok(self, mock_sleep):
|
||||
zone = RoObject(name='zn', serial=314)
|
||||
ds = RoObject(items=[zone])
|
||||
response = RoObject(
|
||||
answer=[RoObject(
|
||||
name='zn',
|
||||
rdclass=dns.rdataclass.IN,
|
||||
rdtype=dns.rdatatype.SOA,
|
||||
to_rdataset=mock.Mock(return_value=ds)
|
||||
)],
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR)
|
||||
)
|
||||
self.notify._make_and_send_dns_message = mock.Mock(
|
||||
return_value=(response, 1)
|
||||
)
|
||||
|
||||
out = self.notify.get_serial_number(
|
||||
'context', zone, 'h', 1234, 1, 2, 3, 4
|
||||
)
|
||||
|
||||
self.assertEqual(('SUCCESS', 314, 3), out)
|
||||
|
||||
@mock.patch('time.sleep')
|
||||
def test_get_serial_number_too_many_retries(self, mock_sleep):
|
||||
zone = RoObject(name='zn', serial=314)
|
||||
ds = RoObject(items=[RoObject(serial=310)])
|
||||
response = RoObject(
|
||||
answer=[RoObject(
|
||||
name='zn',
|
||||
rdclass=dns.rdataclass.IN,
|
||||
rdtype=dns.rdatatype.SOA,
|
||||
to_rdataset=mock.Mock(return_value=ds)
|
||||
)],
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR)
|
||||
)
|
||||
self.notify._make_and_send_dns_message = mock.Mock(
|
||||
return_value=(response, 1)
|
||||
)
|
||||
|
||||
out = self.notify.get_serial_number(
|
||||
'context', zone, 'h', 1234, 1, 2, 3, 4
|
||||
)
|
||||
|
||||
self.assertEqual(('ERROR', 310, 0), out)
|
||||
|
||||
@mock.patch('time.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')
|
||||
mock_send_dns_message.side_effect = dns.exception.Timeout
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
)
|
||||
|
||||
self.assertEqual((None, 3), out)
|
||||
|
||||
@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='')
|
||||
mock_send_dns_message.side_effect = notify.dns_query.BadResponse
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
)
|
||||
|
||||
self.assertEqual((None, 1), out)
|
||||
|
||||
@mock.patch('time.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')
|
||||
socket_error = socket.error()
|
||||
socket_error.errno = socket.errno.EAGAIN
|
||||
mock_send_dns_message.side_effect = socket_error
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
)
|
||||
|
||||
self.assertEqual((None, 3), out)
|
||||
|
||||
@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')
|
||||
socket_error = socket.error()
|
||||
socket_error.errno = socket.errno.ECONNREFUSED
|
||||
# socket errors other than EAGAIN should raise
|
||||
mock_send_dns_message.side_effect = socket_error
|
||||
|
||||
self.assertRaises(
|
||||
socket.error,
|
||||
self.notify._make_and_send_dns_message,
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
)
|
||||
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_nxdomain(self, mock_send_dns_message):
|
||||
zone = RoObject(name='zn')
|
||||
response = RoObject(rcode=mock.Mock(return_value=dns.rcode.NXDOMAIN))
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
)
|
||||
|
||||
self.assertEqual((response, 1), out)
|
||||
|
||||
@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')
|
||||
response = RoObject(
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
|
||||
# rcode is NOERROR but (flags & dns.flags.AA) gives 0
|
||||
flags=0,
|
||||
answer=['answer'],
|
||||
)
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
)
|
||||
|
||||
self.assertEqual((None, 1), out)
|
||||
|
||||
@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')
|
||||
response = RoObject(
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
|
||||
# rcode is NOERROR but flags are not NOERROR
|
||||
flags=123,
|
||||
ednsflags=321,
|
||||
answer=['answer'],
|
||||
)
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
out = self.notify._make_and_send_dns_message(
|
||||
zone, 'host', 123, 1, 2, 3
|
||||
)
|
||||
|
||||
self.assertEqual((None, 1), out)
|
||||
|
||||
def test_make_dns_message(self):
|
||||
msg = self.notify._make_dns_message('zone_name')
|
||||
txt = msg.to_text().split('\n')[1:]
|
||||
|
||||
self.assertEqual([
|
||||
'opcode QUERY',
|
||||
'rcode NOERROR',
|
||||
'flags RD',
|
||||
';QUESTION',
|
||||
'zone_name. IN SOA',
|
||||
';ANSWER',
|
||||
';AUTHORITY',
|
||||
';ADDITIONAL'
|
||||
], txt)
|
||||
|
||||
def test_make_dns_message_notify(self):
|
||||
msg = self.notify._make_dns_message('zone_name', notify=True)
|
||||
txt = msg.to_text().split('\n')[1:]
|
||||
|
||||
self.assertEqual([
|
||||
'opcode NOTIFY',
|
||||
'rcode NOERROR',
|
||||
'flags AA',
|
||||
';QUESTION',
|
||||
'zone_name. IN SOA',
|
||||
';ANSWER',
|
||||
';AUTHORITY',
|
||||
';ADDITIONAL',
|
||||
], txt)
|
@ -44,12 +44,10 @@ class MdnsServiceTest(oslotest.base.BaseTestCase):
|
||||
self.service = service.Service()
|
||||
|
||||
@mock.patch.object(designate.service.DNSService, 'start')
|
||||
@mock.patch.object(designate.service.RPCService, 'start')
|
||||
def test_service_start(self, mock_rpc_start, mock_dns_start):
|
||||
def test_service_start(self, mock_dns_start):
|
||||
self.service.start()
|
||||
|
||||
self.assertTrue(mock_dns_start.called)
|
||||
self.assertTrue(mock_rpc_start.called)
|
||||
|
||||
def test_service_stop(self):
|
||||
self.service.dns_service.stop = mock.Mock()
|
||||
@ -63,14 +61,6 @@ class MdnsServiceTest(oslotest.base.BaseTestCase):
|
||||
def test_service_name(self):
|
||||
self.assertEqual('mdns', self.service.service_name)
|
||||
|
||||
def test_mdns_rpc_topic(self):
|
||||
CONF.set_override('topic', 'test-topic', 'service:mdns')
|
||||
|
||||
self.service = service.Service()
|
||||
|
||||
self.assertEqual('test-topic', self.service.rpc_topic)
|
||||
self.assertEqual('mdns', self.service.service_name)
|
||||
|
||||
@mock.patch.object(storage, 'get_storage')
|
||||
def test_storage_driver(self, mock_get_driver):
|
||||
self.service._storage = None
|
||||
|
@ -1,57 +0,0 @@
|
||||
# Copyright 2019 Inspur
|
||||
#
|
||||
# Author: ZhouHeng <zhouhenglc@inspur.com>
|
||||
#
|
||||
# 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.
|
||||
from unittest import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as cfg_fixture
|
||||
import oslotest.base
|
||||
|
||||
|
||||
from designate import dnsutils
|
||||
from designate.mdns import xfr
|
||||
from designate import objects
|
||||
from designate.tests import fixtures
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class MdnsXFRMixinTest(oslotest.base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(MdnsXFRMixinTest, self).setUp()
|
||||
self.stdlog = fixtures.StandardLogging()
|
||||
self.useFixture(self.stdlog)
|
||||
self.useFixture(cfg_fixture.Config(CONF))
|
||||
self.context = mock.Mock()
|
||||
self.tg = mock.Mock()
|
||||
self.xfrMixin = xfr.XFRMixin()
|
||||
self.xfrMixin.central_api = mock.Mock()
|
||||
|
||||
def test_zone_sync_not_change_name(self):
|
||||
zone = objects.Zone(id='7592878e-4ade-40de-8b8d-699b871ee6fa',
|
||||
name="example.com.",
|
||||
serial=1,
|
||||
masters=objects.ZoneMasterList.from_list([
|
||||
{'host': '127.0.0.1', 'port': 53}, ]))
|
||||
|
||||
with mock.patch.object(dnsutils, 'do_axfr') as mock_axfr, \
|
||||
mock.patch.object(dnsutils, 'from_dnspython_zone') as mock2:
|
||||
mock_axfr.return_value = mock.Mock()
|
||||
mock2.return_value = zone
|
||||
self.xfrMixin.zone_sync(self.context, zone)
|
||||
|
||||
self.assertIn("transferred_at", zone.obj_what_changed())
|
||||
self.assertNotIn("name", zone.obj_what_changed())
|
@ -1,125 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2016 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
import time
|
||||
from unittest import mock
|
||||
|
||||
import monascastatsd
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as cfg_fixture
|
||||
|
||||
from designate import metrics
|
||||
from designate.metrics_client import noop
|
||||
from designate.tests import fixtures
|
||||
from designate.tests import TestCase
|
||||
|
||||
|
||||
class TestNoopMetrics(TestCase):
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
self.stdlog = fixtures.StandardLogging()
|
||||
self.useFixture(self.stdlog)
|
||||
self.CONF = self.useFixture(cfg_fixture.Config(cfg.CONF)).conf
|
||||
self.CONF.set_override('enabled', False, 'monasca:statsd')
|
||||
|
||||
def test_monasca_metrics_disabled(self):
|
||||
self.metrics = metrics.Metrics()
|
||||
self.assertIsInstance(self.metrics.client, noop.Client)
|
||||
self.assertIn('Statsd disabled', self.stdlog.logger.output)
|
||||
|
||||
def test_noop_metrics_client_getters(self):
|
||||
self.metrics = metrics.Metrics()
|
||||
self.assertIsInstance(self.metrics.counter('name'), noop.NoopCounter)
|
||||
self.assertIsInstance(self.metrics.gauge(), noop.NoopGauge)
|
||||
self.assertIsInstance(self.metrics.timer(), noop.NoopTimer)
|
||||
self.assertIsNotNone(self.metrics.timer.__self__)
|
||||
|
||||
def test_noop_metrics_client_timed(self):
|
||||
self.metrics = metrics.Metrics()
|
||||
timer = self.metrics.client.get_timer()
|
||||
|
||||
def func(a):
|
||||
start_time = time.time()
|
||||
try:
|
||||
return a
|
||||
finally:
|
||||
timer.timing('mdns.xfr.zone_sync', time.time() - start_time)
|
||||
|
||||
result = func(1)
|
||||
self.assertEqual(result, 1)
|
||||
|
||||
|
||||
class TestMonascaMetrics(TestCase):
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
self.stdlog = fixtures.StandardLogging()
|
||||
self.useFixture(self.stdlog)
|
||||
self.CONF = self.useFixture(cfg_fixture.Config(cfg.CONF)).conf
|
||||
self.CONF.set_override('enabled', True, 'monasca:statsd')
|
||||
|
||||
@mock.patch('socket.socket.connect')
|
||||
def test_monasca_metrics_enabled(self, conn_mock):
|
||||
self.metrics = metrics.Metrics()
|
||||
|
||||
self.assertIsInstance(self.metrics.client, monascastatsd.client.Client)
|
||||
self.assertIn('Statsd reports to 127.0.0.1:8125',
|
||||
self.stdlog.logger.output)
|
||||
self.assertTrue(conn_mock.called)
|
||||
|
||||
@mock.patch('socket.socket.connect')
|
||||
def test_monasca_metrics_client_getters(self, conn_mock):
|
||||
self.metrics = metrics.Metrics()
|
||||
|
||||
self.assertIsInstance(self.metrics.counter('name'),
|
||||
monascastatsd.counter.Counter)
|
||||
self.assertIsInstance(self.metrics.gauge(),
|
||||
monascastatsd.gauge.Gauge)
|
||||
self.assertIsInstance(self.metrics.timer(),
|
||||
monascastatsd.timer.Timer)
|
||||
self.assertIsNotNone(self.metrics.timer.__self__)
|
||||
|
||||
self.assertTrue(conn_mock.called)
|
||||
|
||||
@mock.patch('socket.socket.send')
|
||||
@mock.patch('socket.socket.connect')
|
||||
def test_monasca_metrics_client_timed(self, conn_mock, send_mock):
|
||||
self.metrics = metrics.Metrics()
|
||||
timer = self.metrics.client.get_timer()
|
||||
|
||||
def func(a):
|
||||
start_time = time.time()
|
||||
try:
|
||||
return a
|
||||
finally:
|
||||
timer.timing('mdns.xfr.zone_sync', time.time() - start_time)
|
||||
|
||||
result = func(1)
|
||||
self.assertEqual(result, 1)
|
||||
self.assertTrue(conn_mock.called)
|
||||
self.assertTrue(send_mock.called)
|
||||
|
||||
def test_monasca_enabled_but_client_not_installed(self):
|
||||
restore = metrics.monascastatsd
|
||||
try:
|
||||
metrics.monascastatsd = None
|
||||
self.metrics = metrics.Metrics()
|
||||
self.assertIsInstance(self.metrics.client, noop.Client)
|
||||
self.assertIn(
|
||||
'monasca-statsd client not installed. '
|
||||
'Metrics will be ignored.',
|
||||
self.stdlog.logger.output
|
||||
)
|
||||
finally:
|
||||
metrics.monascastatsd = restore
|
@ -198,15 +198,6 @@ class MockPool(object):
|
||||
|
||||
|
||||
# Fixtures
|
||||
fx_mdns_api = fixtures.MockPatch('designate.central.service.mdns_rpcapi')
|
||||
|
||||
mdns_api = mock.PropertyMock(
|
||||
return_value=mock.NonCallableMagicMock(spec_set=[
|
||||
'a'
|
||||
])
|
||||
)
|
||||
|
||||
|
||||
fx_worker = fixtures.MockPatch(
|
||||
'designate.central.service.worker_rpcapi.WorkerAPI.get_instance',
|
||||
mock.MagicMock(spec_set=[
|
||||
@ -281,12 +272,6 @@ class CentralBasic(TestCase):
|
||||
|
||||
|
||||
class CentralServiceTestCase(CentralBasic):
|
||||
|
||||
def test_mdns_api_patch(self):
|
||||
with fx_mdns_api:
|
||||
q = self.service.mdns_api
|
||||
assert 'mdns_rpcapi.MdnsAPI.get_instance' in repr(q)
|
||||
|
||||
def test_conf_fixture(self):
|
||||
assert 'service:central' in designate.central.service.cfg.CONF
|
||||
|
||||
@ -1017,13 +1002,14 @@ class CentralZoneTestCase(CentralBasic):
|
||||
masters=[RoObject(host='10.0.0.1', port=53)],
|
||||
serial=1,
|
||||
)
|
||||
with fx_mdns_api:
|
||||
self.service.mdns_api.get_serial_number.return_value = \
|
||||
"SUCCESS", 2, 1
|
||||
with fx_worker:
|
||||
self.service.worker_api.get_serial_number.return_value = (
|
||||
'SUCCESS', 2
|
||||
)
|
||||
self.service.xfr_zone(
|
||||
self.context, CentralZoneTestCase.zone__id)
|
||||
self.assertTrue(
|
||||
self.service.mdns_api.perform_zone_xfr.called)
|
||||
self.service.worker_api.perform_zone_xfr.called)
|
||||
|
||||
self.assertTrue(designate.central.service.policy.check.called)
|
||||
self.assertEqual(
|
||||
|
@ -24,6 +24,7 @@ import dns.rdatatype
|
||||
import dns.zone
|
||||
import eventlet
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as cfg_fixture
|
||||
import oslotest.base
|
||||
|
||||
from designate import dnsutils
|
||||
@ -212,10 +213,52 @@ class TestUtils(designate.tests.TestCase):
|
||||
# This needs to be a one item tuple for the serialization middleware
|
||||
self.assertEqual(middleware.process_request(notify), (response,))
|
||||
|
||||
def test_all_tcp_default(self):
|
||||
self.assertEqual(False, dnsutils.use_all_tcp())
|
||||
|
||||
def test_all_tcp_using_mdns(self):
|
||||
CONF.set_override('all_tcp', True, 'service:mdns')
|
||||
self.assertEqual(True, dnsutils.use_all_tcp())
|
||||
|
||||
def test_all_tcp_using_worker(self):
|
||||
CONF.set_override('all_tcp', True, 'service:worker')
|
||||
self.assertEqual(True, dnsutils.use_all_tcp())
|
||||
|
||||
@mock.patch.object(dns.query, 'udp')
|
||||
def test_send_soa_message(self, mock_udp):
|
||||
dnsutils.soa('zone_name', '192.0.2.1', 1234, 1)
|
||||
msg = mock_udp.call_args[0][0]
|
||||
mock_udp.assert_called_with(
|
||||
mock.ANY, '192.0.2.1', port=1234, timeout=1
|
||||
)
|
||||
txt = msg.to_text().split('\n')[1:]
|
||||
self.assertEqual([
|
||||
'opcode QUERY',
|
||||
'rcode NOERROR',
|
||||
'flags RD',
|
||||
';QUESTION',
|
||||
'zone_name. IN SOA',
|
||||
';ANSWER',
|
||||
';AUTHORITY',
|
||||
';ADDITIONAL'
|
||||
], txt)
|
||||
|
||||
|
||||
class TestDoAfxr(oslotest.base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestDoAfxr, self).setUp()
|
||||
self.useFixture(cfg_fixture.Config(CONF))
|
||||
|
||||
def test_xfr_default(self):
|
||||
self.assertEqual(10, dnsutils.xfr_timeout())
|
||||
|
||||
def test_xfr_timeout_set_using_mdns(self):
|
||||
CONF.set_override('xfr_timeout', 30, 'service:mdns')
|
||||
self.assertEqual(30, dnsutils.xfr_timeout())
|
||||
|
||||
def test_xfr_timeout_set_using_worker(self):
|
||||
CONF.set_override('xfr_timeout', 40, 'service:worker')
|
||||
self.assertEqual(40, dnsutils.xfr_timeout())
|
||||
|
||||
@mock.patch.object(dns.query, 'xfr')
|
||||
@mock.patch.object(dns.zone, 'from_xfr')
|
||||
|
206
designate/tests/unit/workers/test_notify.py
Normal file
206
designate/tests/unit/workers/test_notify.py
Normal file
@ -0,0 +1,206 @@
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Federico Ceratto <federico.ceratto@hpe.com>
|
||||
#
|
||||
# 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.
|
||||
import socket
|
||||
from unittest import mock
|
||||
|
||||
import dns
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as cfg_fixture
|
||||
import oslotest.base
|
||||
|
||||
from designate import dnsutils
|
||||
from designate.tests.unit import RoObject
|
||||
from designate.worker.tasks import zone as worker_zone
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class WorkerNotifyTest(oslotest.base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(WorkerNotifyTest, self).setUp()
|
||||
self.useFixture(cfg_fixture.Config(CONF))
|
||||
self.zone = RoObject(name='zn', serial=314)
|
||||
self.notify = worker_zone.GetZoneSerial(
|
||||
mock.Mock(), mock.Mock(), self.zone, 'localhost', 1234
|
||||
)
|
||||
|
||||
@mock.patch('time.sleep', mock.Mock())
|
||||
def test_get_serial_number_nxdomain(self):
|
||||
CONF.set_override('serial_timeout', 0.1, 'service:worker')
|
||||
|
||||
# The zone is not found but it was supposed to be there
|
||||
response = RoObject(
|
||||
answer=[RoObject(
|
||||
rdclass=dns.rdataclass.IN,
|
||||
rdtype=dns.rdatatype.SOA
|
||||
)],
|
||||
rcode=mock.Mock(return_value=dns.rcode.NXDOMAIN)
|
||||
)
|
||||
zone = RoObject(name='zn', serial=314)
|
||||
notify = worker_zone.GetZoneSerial(mock.Mock(), mock.Mock(),
|
||||
zone, 'localhost',
|
||||
1234)
|
||||
notify._make_and_send_soa_message = mock.Mock(
|
||||
return_value=response
|
||||
)
|
||||
|
||||
self.assertEqual(('NO_ZONE', None), notify())
|
||||
|
||||
@mock.patch('time.sleep', mock.Mock())
|
||||
def test_get_serial_number_nxdomain_deleted_zone(self):
|
||||
# The zone is not found and it's not was supposed be there
|
||||
response = RoObject(
|
||||
answer=[RoObject(
|
||||
rdclass=dns.rdataclass.IN,
|
||||
rdtype=dns.rdatatype.SOA
|
||||
)],
|
||||
rcode=mock.Mock(return_value=dns.rcode.NXDOMAIN)
|
||||
)
|
||||
zone = RoObject(name='zn', serial=0, action='DELETE')
|
||||
notify = worker_zone.GetZoneSerial(mock.Mock(), mock.Mock(),
|
||||
zone, 'localhost',
|
||||
1234)
|
||||
notify._make_and_send_soa_message = mock.Mock(
|
||||
return_value=response
|
||||
)
|
||||
self.assertEqual(('NO_ZONE', 0), notify())
|
||||
|
||||
@mock.patch('time.sleep', mock.Mock())
|
||||
def test_get_serial_number_ok(self):
|
||||
zone = RoObject(name='zn', serial=314)
|
||||
ds = RoObject(items=[zone])
|
||||
response = RoObject(
|
||||
answer=[RoObject(
|
||||
name='zn',
|
||||
rdclass=dns.rdataclass.IN,
|
||||
rdtype=dns.rdatatype.SOA,
|
||||
to_rdataset=mock.Mock(return_value=ds)
|
||||
)],
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
|
||||
flags=dns.flags.AA,
|
||||
ednsflags=dns.rcode.NOERROR,
|
||||
)
|
||||
notify = worker_zone.GetZoneSerial(mock.Mock(), mock.Mock(),
|
||||
zone, 'localhost',
|
||||
1234)
|
||||
notify._make_and_send_soa_message = mock.Mock(
|
||||
return_value=response
|
||||
)
|
||||
self.assertEqual(('SUCCESS', 314), notify())
|
||||
|
||||
@mock.patch('time.sleep', mock.Mock())
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_error_flags(self,
|
||||
mock_send_dns_message):
|
||||
response = RoObject(
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
|
||||
# rcode is NOERROR but flags are not NOERROR
|
||||
flags=123,
|
||||
ednsflags=321,
|
||||
answer=['answer'],
|
||||
)
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
notify = worker_zone.GetZoneSerial(mock.Mock(), mock.Mock(),
|
||||
self.zone, 'localhost',
|
||||
1234)
|
||||
|
||||
self.assertEqual(('ERROR', None), notify())
|
||||
|
||||
@mock.patch('time.sleep', mock.Mock())
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_missing_AA_flags(self,
|
||||
mock_send_dns_message):
|
||||
response = RoObject(
|
||||
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
|
||||
# rcode is NOERROR but (flags & dns.flags.AA) gives 0
|
||||
flags=0,
|
||||
answer=['answer'],
|
||||
)
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
notify = worker_zone.GetZoneSerial(mock.Mock(), mock.Mock(),
|
||||
self.zone, 'localhost',
|
||||
1234)
|
||||
|
||||
self.assertEqual(('ERROR', None), notify())
|
||||
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_timeout(self, mock_send_dns_message):
|
||||
mock_send_dns_message.side_effect = dns.exception.Timeout
|
||||
|
||||
out = self.notify._make_and_send_soa_message(
|
||||
self.zone.name, 'host', 123
|
||||
)
|
||||
|
||||
self.assertIsNone(out)
|
||||
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_bad_response(self,
|
||||
mock_send_dns_message):
|
||||
self.notify._make_dns_message = mock.Mock(return_value='')
|
||||
mock_send_dns_message.side_effect = dns.query.BadResponse
|
||||
|
||||
out = self.notify._make_and_send_soa_message(
|
||||
self.zone.name, 'host', 123
|
||||
)
|
||||
|
||||
self.assertIsNone(out)
|
||||
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_eagain(self, mock_send_dns_message):
|
||||
# bug #1558096
|
||||
socket_error = socket.error()
|
||||
socket_error.errno = socket.errno.EAGAIN
|
||||
mock_send_dns_message.side_effect = socket_error
|
||||
|
||||
out = self.notify._make_and_send_soa_message(
|
||||
self.zone.name, 'host', 123
|
||||
)
|
||||
|
||||
self.assertIsNone(out)
|
||||
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_econnrefused(self,
|
||||
mock_send_dns_message):
|
||||
# bug #1558096
|
||||
socket_error = socket.error()
|
||||
socket_error.errno = socket.errno.ECONNREFUSED
|
||||
# socket errors other than EAGAIN should raise
|
||||
mock_send_dns_message.side_effect = socket_error
|
||||
|
||||
self.assertRaises(
|
||||
socket.error,
|
||||
self.notify._make_and_send_soa_message,
|
||||
self.zone.name, 'host', 123
|
||||
)
|
||||
|
||||
@mock.patch.object(dnsutils, 'send_dns_message')
|
||||
def test_make_and_send_dns_message_nxdomain(self, mock_send_dns_message):
|
||||
response = RoObject(
|
||||
rcode=mock.Mock(return_value=dns.rcode.NXDOMAIN),
|
||||
flags=dns.flags.AA,
|
||||
ednsflags=dns.rcode.NXDOMAIN
|
||||
)
|
||||
mock_send_dns_message.return_value = response
|
||||
|
||||
out = self.notify._make_and_send_soa_message(
|
||||
self.zone.name, 'host', 123
|
||||
)
|
||||
|
||||
self.assertEqual(response, out)
|
@ -286,3 +286,36 @@ class TestService(oslotest.base.BaseTestCase):
|
||||
)
|
||||
|
||||
self.service.executor.run.assert_called_with(mock_export_zone())
|
||||
|
||||
@mock.patch.object(service.zonetasks, 'ZoneXfr')
|
||||
def test_perform_zone_xfr(self, mock_perform_zone_xfr):
|
||||
self.service._executor = mock.Mock()
|
||||
self.service._pool = mock.Mock()
|
||||
zone = mock.Mock()
|
||||
|
||||
self.service.perform_zone_xfr(self.context, zone)
|
||||
|
||||
mock_perform_zone_xfr.assert_called_with(
|
||||
self.service.executor,
|
||||
self.context,
|
||||
zone,
|
||||
None
|
||||
)
|
||||
|
||||
self.service.executor.run.assert_called_with(mock_perform_zone_xfr())
|
||||
|
||||
@mock.patch.object(service.zonetasks, 'GetZoneSerial')
|
||||
def test_get_serial_number(self, mock_get_serial_number):
|
||||
zone = mock.Mock()
|
||||
|
||||
self.service.get_serial_number(
|
||||
self.context, zone, 'localhost', 53
|
||||
)
|
||||
|
||||
mock_get_serial_number.assert_called_with(
|
||||
self.service.executor,
|
||||
self.context,
|
||||
zone,
|
||||
'localhost',
|
||||
53
|
||||
)
|
||||
|
102
designate/tests/unit/workers/test_xfr.py
Normal file
102
designate/tests/unit/workers/test_xfr.py
Normal file
@ -0,0 +1,102 @@
|
||||
# Copyright 2019 Inspur
|
||||
#
|
||||
# Author: ZhouHeng <zhouhenglc@inspur.com>
|
||||
#
|
||||
# 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.
|
||||
from unittest import mock
|
||||
|
||||
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 import fixtures
|
||||
from designate.worker.tasks import zone as worker_zone
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestXfr(oslotest.base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestXfr, self).setUp()
|
||||
self.stdlog = fixtures.StandardLogging()
|
||||
self.useFixture(self.stdlog)
|
||||
self.useFixture(cfg_fixture.Config(CONF))
|
||||
self.context = mock.Mock()
|
||||
|
||||
@mock.patch.object(dnsutils, 'do_axfr', mock.Mock())
|
||||
def test_zone_sync_not_change_name(self):
|
||||
zone = objects.Zone(
|
||||
id='7592878e-4ade-40de-8b8d-699b871ee6fa',
|
||||
name='example.com.',
|
||||
serial=1,
|
||||
masters=objects.ZoneMasterList.from_list(
|
||||
[{'host': '127.0.0.1', 'port': 53}, ]
|
||||
)
|
||||
)
|
||||
|
||||
self.xfr = worker_zone.ZoneXfr(mock.Mock(), self.context, zone)
|
||||
self.xfr._central_api = mock.Mock()
|
||||
|
||||
with mock.patch.object(dnsutils, 'from_dnspython_zone') as mock_dns:
|
||||
mock_dns.return_value = zone
|
||||
|
||||
self.xfr()
|
||||
|
||||
self.assertIn('transferred_at', zone.obj_what_changed())
|
||||
self.assertNotIn('name', zone.obj_what_changed())
|
||||
|
||||
@mock.patch.object(dnsutils, 'do_axfr', mock.Mock())
|
||||
def test_zone_sync_using_list_of_servers(self):
|
||||
zone = objects.Zone(
|
||||
id='7592878e-4ade-40de-8b8d-699b871ee6fa',
|
||||
name='example.com.',
|
||||
serial=1,
|
||||
)
|
||||
|
||||
self.xfr = worker_zone.ZoneXfr(
|
||||
mock.Mock(), self.context, zone,
|
||||
servers=[{'host': '127.0.0.1', 'port': 53}, ]
|
||||
)
|
||||
self.xfr._central_api = mock.Mock()
|
||||
|
||||
with mock.patch.object(dnsutils, 'from_dnspython_zone') as mock_dns:
|
||||
mock_dns.return_value = zone
|
||||
|
||||
self.xfr()
|
||||
|
||||
self.assertIn('transferred_at', zone.obj_what_changed())
|
||||
self.assertNotIn('name', zone.obj_what_changed())
|
||||
|
||||
@mock.patch.object(dnsutils, 'do_axfr', side_effect=exceptions.XFRFailure)
|
||||
def test_zone_sync_axfr_failure(self, _):
|
||||
zone = objects.Zone(
|
||||
id='7592878e-4ade-40de-8b8d-699b871ee6fa',
|
||||
name='example.com.',
|
||||
serial=1,
|
||||
masters=objects.ZoneMasterList.from_list(
|
||||
[{'host': '127.0.0.1', 'port': 53}, ]
|
||||
)
|
||||
)
|
||||
|
||||
self.xfr = worker_zone.ZoneXfr(mock.Mock(), self.context, zone)
|
||||
self.xfr._central_api = mock.Mock()
|
||||
|
||||
with mock.patch.object(dnsutils, 'from_dnspython_zone') as mock_dns:
|
||||
mock_dns.return_value = zone
|
||||
|
||||
self.xfr()
|
||||
|
||||
self.assertNotIn('transferred_at', zone.obj_what_changed())
|
@ -35,15 +35,16 @@ class WorkerAPI(object):
|
||||
API version history:
|
||||
|
||||
1.0 - Initial version
|
||||
1.1 - Added perform_zone_xfr and get_serial_number
|
||||
"""
|
||||
RPC_API_VERSION = '1.0'
|
||||
RPC_API_VERSION = '1.1'
|
||||
|
||||
def __init__(self, topic=None):
|
||||
self.topic = topic if topic else cfg.CONF['service:worker'].topic
|
||||
|
||||
target = messaging.Target(topic=self.topic,
|
||||
version=self.RPC_API_VERSION)
|
||||
self.client = rpc.get_client(target, version_cap='1.0')
|
||||
self.client = rpc.get_client(target, version_cap='1.1')
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
@ -78,3 +79,13 @@ class WorkerAPI(object):
|
||||
def start_zone_export(self, context, zone, export):
|
||||
return self.client.cast(
|
||||
context, 'start_zone_export', zone=zone, export=export)
|
||||
|
||||
def perform_zone_xfr(self, context, zone, servers=None):
|
||||
return self.client.cast(
|
||||
context, 'perform_zone_xfr', zone=zone, servers=servers)
|
||||
|
||||
def get_serial_number(self, context, zone, host, port):
|
||||
return self.client.call(
|
||||
context, 'get_serial_number', zone=zone,
|
||||
host=host, port=port,
|
||||
)
|
||||
|
@ -42,7 +42,7 @@ class AlsoNotifyTask(object):
|
||||
|
||||
|
||||
class Service(service.RPCService):
|
||||
RPC_API_VERSION = '1.0'
|
||||
RPC_API_VERSION = '1.1'
|
||||
|
||||
target = messaging.Target(version=RPC_API_VERSION)
|
||||
|
||||
@ -143,10 +143,10 @@ class Service(service.RPCService):
|
||||
|
||||
def _do_zone_action(self, context, zone):
|
||||
pool = self.get_pool(zone.pool_id)
|
||||
all_tasks = []
|
||||
all_tasks.append(zonetasks.ZoneAction(
|
||||
self.executor, context, pool, zone, zone.action
|
||||
))
|
||||
all_tasks = [
|
||||
zonetasks.ZoneAction(self.executor, context, pool, zone,
|
||||
zone.action)
|
||||
]
|
||||
|
||||
# Send a NOTIFY to each also-notifies
|
||||
for also_notify in pool.also_notifies:
|
||||
@ -206,3 +206,26 @@ class Service(service.RPCService):
|
||||
return self.executor.run(zonetasks.ExportZone(
|
||||
self.executor, context, zone, export
|
||||
))
|
||||
|
||||
@rpc.expected_exceptions()
|
||||
def perform_zone_xfr(self, context, zone, servers=None):
|
||||
"""
|
||||
:param zone: Zone to be exported
|
||||
:param servers:
|
||||
:return: None
|
||||
"""
|
||||
return self.executor.run(zonetasks.ZoneXfr(
|
||||
self.executor, context, zone, servers
|
||||
))
|
||||
|
||||
@rpc.expected_exceptions()
|
||||
def get_serial_number(self, context, zone, host, port):
|
||||
"""
|
||||
:param zone: Zone to get serial number
|
||||
:param host:
|
||||
:param port:
|
||||
:return: tuple
|
||||
"""
|
||||
return self.executor.run(zonetasks.GetZoneSerial(
|
||||
self.executor, context, zone, host, port,
|
||||
))[0]
|
||||
|
@ -14,14 +14,18 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from collections import namedtuple
|
||||
import errno
|
||||
import socket
|
||||
import time
|
||||
|
||||
import dns
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from designate import dnsutils
|
||||
from designate import exceptions
|
||||
from designate import objects
|
||||
from designate import utils
|
||||
from designate.worker.tasks import base
|
||||
|
||||
@ -150,6 +154,36 @@ class SendNotify(base.Task):
|
||||
return False
|
||||
|
||||
|
||||
class ZoneXfr(base.Task):
|
||||
"""
|
||||
Perform AXFR on Zone
|
||||
"""
|
||||
|
||||
def __init__(self, executor, context, zone, servers=None):
|
||||
super(ZoneXfr, self).__init__(executor)
|
||||
self.context = context
|
||||
self.zone = zone
|
||||
self.servers = servers
|
||||
|
||||
def __call__(self):
|
||||
servers = self.servers or self.zone.masters
|
||||
if isinstance(servers, objects.ListObjectMixin):
|
||||
servers = servers.to_list()
|
||||
|
||||
try:
|
||||
dnspython_zone = dnsutils.do_axfr(self.zone.name, servers)
|
||||
except exceptions.XFRFailure as e:
|
||||
LOG.warning(e)
|
||||
return
|
||||
|
||||
self.zone.update(dnsutils.from_dnspython_zone(dnspython_zone))
|
||||
self.zone.transferred_at = timeutils.utcnow()
|
||||
self.zone.obj_reset_changes(['name'])
|
||||
self.central_api.update_zone(
|
||||
self.context, self.zone, increment_serial=False
|
||||
)
|
||||
|
||||
|
||||
class ZoneActor(base.Task):
|
||||
"""
|
||||
Orchestrate the Create/Update/Delete action on targets and update status
|
||||
@ -558,6 +592,136 @@ class ZonePoller(base.Task):
|
||||
return result
|
||||
|
||||
|
||||
class GetZoneSerial(base.Task):
|
||||
"""
|
||||
Get zone serial number from a resolver using retries.
|
||||
"""
|
||||
def __init__(self, executor, context, zone, host, port):
|
||||
super(GetZoneSerial, self).__init__(executor)
|
||||
self.context = context
|
||||
self.zone = zone
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.serial_max_retries = CONF['service:worker'].serial_max_retries
|
||||
self.serial_retry_delay = CONF['service:worker'].serial_retry_delay
|
||||
self.serial_timeout = CONF['service:worker'].serial_timeout
|
||||
|
||||
def __call__(self):
|
||||
LOG.debug(
|
||||
'Sending SOA for zone_name=%(zone)s to %(server)s:%(port)d.',
|
||||
{
|
||||
'zone': self.zone.name,
|
||||
'server': self.host,
|
||||
'port': self.port,
|
||||
}
|
||||
)
|
||||
actual_serial = None
|
||||
status = 'ERROR'
|
||||
for retry in range(0, self.serial_max_retries):
|
||||
response = self._make_and_send_soa_message(
|
||||
self.zone.name, self.host, self.port
|
||||
)
|
||||
if not response:
|
||||
pass
|
||||
elif (response.rcode() in (
|
||||
dns.rcode.NXDOMAIN,
|
||||
dns.rcode.REFUSED,
|
||||
dns.rcode.SERVFAIL) or not bool(response.answer)):
|
||||
status = 'NO_ZONE'
|
||||
if (self.zone.serial == 0 and
|
||||
self.zone.action in ('DELETE', 'NONE')):
|
||||
actual_serial = 0
|
||||
break
|
||||
elif not (response.flags & dns.flags.AA):
|
||||
LOG.warning(
|
||||
'Unable to get serial for zone_name=%(zone)s '
|
||||
'to %(server)s:%(port)d. '
|
||||
'Unable to get an Authoritative Answer from server.',
|
||||
{
|
||||
'zone': self.zone.name,
|
||||
'server': self.host,
|
||||
'port': self.port,
|
||||
}
|
||||
)
|
||||
break
|
||||
elif dns.rcode.from_flags(
|
||||
response.flags, response.ednsflags) != dns.rcode.NOERROR:
|
||||
pass
|
||||
elif (len(response.answer) == 1 and
|
||||
str(response.answer[0].name) == self.zone.name and
|
||||
response.answer[0].rdclass == dns.rdataclass.IN and
|
||||
response.answer[0].rdtype == dns.rdatatype.SOA):
|
||||
rrset = response.answer[0]
|
||||
actual_serial = list(rrset.to_rdataset().items)[0].serial
|
||||
|
||||
if actual_serial is not None:
|
||||
status = 'SUCCESS'
|
||||
break
|
||||
time.sleep(self.serial_retry_delay)
|
||||
|
||||
if actual_serial is None:
|
||||
LOG.warning(
|
||||
'Unable to get serial for zone_name=%(zone)s'
|
||||
'to %(server)s:%(port)d.',
|
||||
{
|
||||
'zone': self.zone.name,
|
||||
'server': self.host,
|
||||
'port': self.port,
|
||||
}
|
||||
)
|
||||
|
||||
return status, actual_serial
|
||||
|
||||
def _make_and_send_soa_message(self, zone_name, host, port):
|
||||
"""
|
||||
Generate and send a SOA message.
|
||||
|
||||
:param zone_name: The zone name.
|
||||
:param host: The destination host for the dns message.
|
||||
:param port: The destination port for the dns message.
|
||||
"""
|
||||
try:
|
||||
return dnsutils.soa(
|
||||
zone_name, host, port, timeout=self.serial_timeout
|
||||
)
|
||||
except socket.error as e:
|
||||
if e.errno != errno.EAGAIN:
|
||||
raise
|
||||
LOG.info(
|
||||
'Got EAGAIN while trying to send SOA for '
|
||||
'zone_name=%(zone_name)s to %(server)s:%(port)d. '
|
||||
'timeout=%(timeout)d seconds.',
|
||||
{
|
||||
'zone_name': zone_name,
|
||||
'server': host,
|
||||
'port': port,
|
||||
'timeout': self.serial_timeout
|
||||
}
|
||||
)
|
||||
except dns.exception.Timeout:
|
||||
LOG.warning(
|
||||
'Got Timeout while trying to send SOA for '
|
||||
'zone_name=%(zone_name)s to %(server)s:%(port)d. '
|
||||
'timeout=%(timeout)d seconds.',
|
||||
{
|
||||
'zone_name': zone_name,
|
||||
'server': host,
|
||||
'port': port,
|
||||
'timeout': self.serial_timeout
|
||||
}
|
||||
)
|
||||
except dns.query.BadResponse:
|
||||
LOG.warning(
|
||||
'Got BadResponse while trying to send SOA '
|
||||
'for zone_name=%(zone_name)s to %(server)s:%(port)d.',
|
||||
{
|
||||
'zone_name': zone_name,
|
||||
'server': host,
|
||||
'port': port,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
###################
|
||||
# Status Management
|
||||
###################
|
||||
|
@ -104,8 +104,6 @@ How do I monitor Designate?
|
||||
Designate can be monitored by various
|
||||
`monitoring systems listed here <https://wiki.openstack.org/wiki/Operations/Monitoring>`_
|
||||
|
||||
OpenStack recommends `Monasca <https://wiki.openstack.org/wiki/Monasca>`_
|
||||
|
||||
What are useful metrics to monitor?
|
||||
-----------------------------------
|
||||
|
||||
|
@ -16,7 +16,6 @@ Contents:
|
||||
Designate Tempest Plugin <https://docs.openstack.org/designate-tempest-plugin/latest>
|
||||
architecture
|
||||
gmr
|
||||
metrics
|
||||
sourcedoc/index
|
||||
ubuntu-dev
|
||||
integrations
|
||||
|
@ -1,14 +0,0 @@
|
||||
.. _metrics:
|
||||
|
||||
****************************
|
||||
Monasca-Statsd based Metrics
|
||||
****************************
|
||||
|
||||
metrics Base
|
||||
============
|
||||
.. automodule:: designate.metrics
|
||||
:members:
|
||||
:special-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
@ -4,13 +4,6 @@
|
||||
MDNS
|
||||
****
|
||||
|
||||
MDNS Base
|
||||
=========
|
||||
.. automodule:: designate.mdns.base
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
MDNS Handler
|
||||
============
|
||||
.. automodule:: designate.mdns.handler
|
||||
@ -18,19 +11,6 @@ MDNS Handler
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
MDNS Notify
|
||||
===========
|
||||
.. automodule:: designate.mdns.notify
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
MDNS RPC API
|
||||
============
|
||||
.. automodule:: designate.mdns.rpcapi
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
MDNS Service
|
||||
============
|
||||
@ -38,10 +18,3 @@ MDNS Service
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
MDNS XFR
|
||||
========
|
||||
.. automodule:: designate.mdns.xfr
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
8
releasenotes/notes/mdns-rpc-moved-0e7eea194064834a.yaml
Normal file
8
releasenotes/notes/mdns-rpc-moved-0e7eea194064834a.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The ``SECONDARY zone`` RPC calls were moved from the ``mdns`` service to ``worker``
|
||||
service. When upgrading multi-controller deployments we recommend that you
|
||||
restart the ``central`` and ``worker`` services first to move the
|
||||
``SECONDARY zone`` calls to the ``worker``, and once both services has been
|
||||
upgraded go ahead and restart the ``mdns`` service.
|
6
releasenotes/notes/removed-metrics-11a53cf88e1ea224.yaml
Normal file
6
releasenotes/notes/removed-metrics-11a53cf88e1ea224.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Removed the ``monascastatsd`` based metrics solution as all calls using
|
||||
it has been changed or removed and designate is no longer tracking
|
||||
any metrics using the metrics endpoint.
|
@ -48,6 +48,5 @@ python-memcached>=1.56 # PSF
|
||||
tooz>=1.58.0 # Apache-2.0
|
||||
debtcollector>=1.19.0 # Apache-2.0
|
||||
os-win>=4.1.0 # Apache-2.0
|
||||
monasca-statsd>=1.4.0 # Apache-2.0
|
||||
futurist>=1.2.0 # Apache-2.0
|
||||
edgegrid-python>=1.1.1 # Apache-2.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user