Merge "Removed RPC calls from MDNS and moved them to the Worker"
This commit is contained in:
@@ -185,7 +185,7 @@ class RequestHandler(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
zone = dnsutils.do_axfr(zone_name, self.masters,
|
zone = dnsutils.do_axfr(zone_name, self.masters,
|
||||||
source=self.transfer_source)
|
source=self.transfer_source)
|
||||||
self.backend.update_zone(zone)
|
self.backend.update_zone(zone)
|
||||||
except Exception:
|
except Exception:
|
||||||
response.set_rcode(dns.rcode.from_text("SERVFAIL"))
|
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.conf.agent import DEFAULT_AGENT_PORT
|
||||||
from designate import dnsutils
|
from designate import dnsutils
|
||||||
from designate import exceptions
|
from designate import exceptions
|
||||||
from designate.mdns import rpcapi as mdns_api
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@@ -59,10 +59,6 @@ class AgentPoolBackend(base.Backend):
|
|||||||
self.max_retries = CONF['service:worker'].poll_max_retries
|
self.max_retries = CONF['service:worker'].poll_max_retries
|
||||||
# FIXME: the agent retries creating zones without any interval
|
# 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):
|
def create_zone(self, context, zone):
|
||||||
LOG.debug('Create Zone')
|
LOG.debug('Create Zone')
|
||||||
response = self._make_and_send_dns_message(
|
response = self._make_and_send_dns_message(
|
||||||
|
@@ -19,7 +19,6 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from designate.context import DesignateContext
|
from designate.context import DesignateContext
|
||||||
from designate.mdns import rpcapi as mdns_api
|
|
||||||
from designate.plugin import DriverPlugin
|
from designate.plugin import DriverPlugin
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@@ -58,10 +57,6 @@ class Backend(DriverPlugin):
|
|||||||
def stop(self):
|
def stop(self):
|
||||||
LOG.info('Stopped %s backend', self.get_canonical_name())
|
LOG.info('Stopped %s backend', self.get_canonical_name())
|
||||||
|
|
||||||
@property
|
|
||||||
def mdns_api(self):
|
|
||||||
return mdns_api.MdnsAPI.get_instance()
|
|
||||||
|
|
||||||
# Core Backend Interface
|
# Core Backend Interface
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def create_zone(self, context, zone):
|
def create_zone(self, context, zone):
|
||||||
|
@@ -37,7 +37,6 @@ from designate import context as dcontext
|
|||||||
from designate import coordination
|
from designate import coordination
|
||||||
from designate import dnsutils
|
from designate import dnsutils
|
||||||
from designate import exceptions
|
from designate import exceptions
|
||||||
from designate.mdns import rpcapi as mdns_rpcapi
|
|
||||||
from designate import network_api
|
from designate import network_api
|
||||||
from designate import notifications
|
from designate import notifications
|
||||||
from designate import objects
|
from designate import objects
|
||||||
@@ -243,10 +242,6 @@ class Service(service.RPCService):
|
|||||||
self.coordination.stop()
|
self.coordination.stop()
|
||||||
super(Service, self).stop(graceful)
|
super(Service, self).stop(graceful)
|
||||||
|
|
||||||
@property
|
|
||||||
def mdns_api(self):
|
|
||||||
return mdns_rpcapi.MdnsAPI.get_instance()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def worker_api(self):
|
def worker_api(self):
|
||||||
return worker_rpcapi.WorkerAPI.get_instance()
|
return worker_rpcapi.WorkerAPI.get_instance()
|
||||||
@@ -957,7 +952,7 @@ class Service(service.RPCService):
|
|||||||
self.worker_api.create_zone(context, zone)
|
self.worker_api.create_zone(context, zone)
|
||||||
|
|
||||||
if zone.type == 'SECONDARY':
|
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
|
# If zone is a superzone, update subzones
|
||||||
# with new parent IDs
|
# with new parent IDs
|
||||||
@@ -1116,7 +1111,7 @@ class Service(service.RPCService):
|
|||||||
|
|
||||||
# Fire off a XFR
|
# Fire off a XFR
|
||||||
if 'masters' in changes:
|
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)
|
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
|
# Ensure the format of the servers are correct, then poll the
|
||||||
# serial
|
# serial
|
||||||
srv = random.choice(zone.masters)
|
srv = random.choice(zone.masters)
|
||||||
status, serial, retries = self.mdns_api.get_serial_number(
|
status, serial = self.worker_api.get_serial_number(
|
||||||
context, zone, srv.host, srv.port, 3, 1, 3, 0)
|
context, zone, srv.host, srv.port)
|
||||||
|
|
||||||
# Perform XFR if serial's are not equal
|
# 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 "
|
LOG.info("Serial %(srv_serial)d is not equal to zone's "
|
||||||
"%(serial)d, performing AXFR",
|
"%(serial)d, performing AXFR",
|
||||||
{"srv_serial": serial, "serial": zone.serial})
|
{"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()
|
@rpc.expected_exceptions()
|
||||||
def count_zones(self, context, criterion=None):
|
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 keystone
|
||||||
from designate.conf import knot2
|
from designate.conf import knot2
|
||||||
from designate.conf import mdns
|
from designate.conf import mdns
|
||||||
from designate.conf import metrics
|
|
||||||
from designate.conf import msdns
|
from designate.conf import msdns
|
||||||
from designate.conf import network_api
|
from designate.conf import network_api
|
||||||
from designate.conf import producer
|
from designate.conf import producer
|
||||||
@@ -54,7 +53,6 @@ infoblox.register_opts(CONF)
|
|||||||
keystone.register_opts(CONF)
|
keystone.register_opts(CONF)
|
||||||
knot2.register_opts(CONF)
|
knot2.register_opts(CONF)
|
||||||
mdns.register_opts(CONF)
|
mdns.register_opts(CONF)
|
||||||
metrics.register_opts(CONF)
|
|
||||||
msdns.register_opts(CONF)
|
msdns.register_opts(CONF)
|
||||||
network_api.register_opts(CONF)
|
network_api.register_opts(CONF)
|
||||||
producer.register_opts(CONF)
|
producer.register_opts(CONF)
|
||||||
|
@@ -33,8 +33,12 @@ MDNS_OPTS = [
|
|||||||
help='mDNS TCP Backlog'),
|
help='mDNS TCP Backlog'),
|
||||||
cfg.FloatOpt('tcp_recv_timeout', default=0.5,
|
cfg.FloatOpt('tcp_recv_timeout', default=0.5,
|
||||||
help='mDNS TCP Receive Timeout'),
|
help='mDNS TCP Receive Timeout'),
|
||||||
cfg.BoolOpt('all_tcp', default=False,
|
cfg.IntOpt('all_tcp', help='Send all traffic over 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,
|
cfg.BoolOpt('query_enforce_tsig', default=False,
|
||||||
help='Enforce all incoming queries (including AXFR) are TSIG '
|
help='Enforce all incoming queries (including AXFR) are TSIG '
|
||||||
'signed'),
|
'signed'),
|
||||||
@@ -45,7 +49,11 @@ MDNS_OPTS = [
|
|||||||
cfg.StrOpt('topic', default='mdns',
|
cfg.StrOpt('topic', default='mdns',
|
||||||
help='RPC topic name for mdns'),
|
help='RPC topic name for mdns'),
|
||||||
cfg.IntOpt('xfr_timeout', help="Timeout in seconds for XFR's.",
|
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'),
|
help='Whether to allow synchronous zone exports'),
|
||||||
cfg.StrOpt('topic', default='worker',
|
cfg.StrOpt('topic', default='worker',
|
||||||
help='RPC topic name for 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
|
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
|
Requests an AXFR for a given zone name and process the response
|
||||||
|
|
||||||
:returns: Zone instance from dnspython
|
:returns: Zone instance from dnspython
|
||||||
"""
|
"""
|
||||||
random.shuffle(servers)
|
random.shuffle(servers)
|
||||||
timeout = timeout or CONF["service:mdns"].xfr_timeout
|
|
||||||
|
|
||||||
xfr = None
|
xfr = None
|
||||||
for srv in servers:
|
for srv in servers:
|
||||||
for address in get_ip_addresses(srv['host']):
|
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}
|
log_info = {'name': zone_name, 'host': srv, 'address': address}
|
||||||
try:
|
try:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
@@ -415,6 +421,24 @@ def notify(zone_name, host, port=53):
|
|||||||
return send_dns_message(msg, host, port=port)
|
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):
|
def send_dns_message(dns_message, host, port=53, timeout=10):
|
||||||
"""
|
"""
|
||||||
Send the dns message and return the response
|
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)
|
ip_address = get_ip_address(host)
|
||||||
# This can raise some exceptions, but we'll catch them elsewhere
|
# 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(
|
return dns.query.udp(
|
||||||
dns_message, ip_address, port=port, timeout=timeout)
|
dns_message, ip_address, port=port, timeout=timeout)
|
||||||
return dns.query.tcp(
|
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_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from designate.central import rpcapi as central_api
|
|
||||||
from designate import exceptions
|
from designate import exceptions
|
||||||
from designate.mdns import xfr
|
from designate.worker import rpcapi as worker_api
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@@ -38,18 +38,18 @@ CONF.import_opt('default_pool_id', 'designate.central',
|
|||||||
TSIG_RRSIZE = 10 + 64 + 160 + 1
|
TSIG_RRSIZE = 10 + 64 + 160 + 1
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(xfr.XFRMixin):
|
class RequestHandler(object):
|
||||||
def __init__(self, storage, tg):
|
def __init__(self, storage, tg):
|
||||||
self._central_api = None
|
self._worker_api = None
|
||||||
|
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.tg = tg
|
self.tg = tg
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def central_api(self):
|
def worker_api(self):
|
||||||
if not self._central_api:
|
if not self._worker_api:
|
||||||
self._central_api = central_api.CentralAPI.get_instance()
|
self._worker_api = worker_api.WorkerAPI.get_instance()
|
||||||
return self._central_api
|
return self._worker_api
|
||||||
|
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
"""
|
"""
|
||||||
@@ -169,8 +169,7 @@ class RequestHandler(xfr.XFRMixin):
|
|||||||
'master_addr': master_addr.to_data()
|
'master_addr': master_addr.to_data()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.tg.add_thread(self.zone_sync, context, zone,
|
self.worker_api.perform_zone_xfr(context, zone, [master_addr])
|
||||||
[master_addr])
|
|
||||||
|
|
||||||
response.flags |= dns.flags.AA
|
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.conf.mdns import DEFAULT_MDNS_PORT
|
||||||
from designate import dnsutils
|
from designate import dnsutils
|
||||||
from designate.mdns import handler
|
from designate.mdns import handler
|
||||||
from designate.mdns import notify
|
|
||||||
from designate.mdns import xfr
|
|
||||||
from designate import service
|
from designate import service
|
||||||
from designate import storage
|
from designate import storage
|
||||||
from designate import utils
|
from designate import utils
|
||||||
@@ -29,20 +27,15 @@ LOG = logging.getLogger(__name__)
|
|||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
class Service(service.RPCService):
|
class Service(service.Service):
|
||||||
_dns_default_port = DEFAULT_MDNS_PORT
|
_dns_default_port = DEFAULT_MDNS_PORT
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._storage = None
|
self._storage = None
|
||||||
|
|
||||||
super(Service, self).__init__(
|
super(Service, self).__init__(
|
||||||
self.service_name, cfg.CONF['service:mdns'].topic,
|
self.service_name, threads=cfg.CONF['service:mdns'].threads,
|
||||||
threads=cfg.CONF['service:mdns'].threads,
|
|
||||||
)
|
)
|
||||||
self.override_endpoints(
|
|
||||||
[notify.NotifyEndpoint(self.tg), xfr.XfrEndpoint(self.tg)]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.dns_service = service.DNSService(
|
self.dns_service = service.DNSService(
|
||||||
self.dns_application, self.tg,
|
self.dns_application, self.tg,
|
||||||
cfg.CONF['service:mdns'].listen,
|
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
|
from designate.common import profiler
|
||||||
import designate.conf
|
import designate.conf
|
||||||
from designate.i18n import _
|
from designate.i18n import _
|
||||||
from designate.metrics import metrics
|
|
||||||
from designate import policy
|
from designate import policy
|
||||||
from designate import rpc
|
from designate import rpc
|
||||||
from designate import utils
|
from designate import utils
|
||||||
@@ -150,7 +149,6 @@ class DNSService(object):
|
|||||||
self.tcp_backlog = tcp_backlog
|
self.tcp_backlog = tcp_backlog
|
||||||
self.tcp_recv_timeout = tcp_recv_timeout
|
self.tcp_recv_timeout = tcp_recv_timeout
|
||||||
self.listen = listen
|
self.listen = listen
|
||||||
metrics.init()
|
|
||||||
|
|
||||||
# Eventet will complain loudly about our use of multiple greentheads
|
# Eventet will complain loudly about our use of multiple greentheads
|
||||||
# reading/writing to the UDP socket at once. Disable this warning.
|
# 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.central import service as central_service
|
||||||
from designate import exceptions
|
from designate import exceptions
|
||||||
from designate.mdns import rpcapi as mdns_api
|
|
||||||
from designate import objects
|
from designate import objects
|
||||||
from designate.tests.test_api.test_v2 import ApiV2TestCase
|
from designate.tests.test_api.test_v2 import ApiV2TestCase
|
||||||
|
from designate.worker import rpcapi as worker_api
|
||||||
|
|
||||||
|
|
||||||
class ApiV2ZonesTest(ApiV2TestCase):
|
class ApiV2ZonesTest(ApiV2TestCase):
|
||||||
@@ -565,16 +565,17 @@ class ApiV2ZonesTest(ApiV2TestCase):
|
|||||||
# Create a zone
|
# Create a zone
|
||||||
zone = self.create_zone(**fixture)
|
zone = self.create_zone(**fixture)
|
||||||
|
|
||||||
mdns = mock.Mock()
|
worker = mock.Mock()
|
||||||
with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
|
with mock.patch.object(worker_api.WorkerAPI,
|
||||||
get_mdns.return_value = mdns
|
'get_instance') as get_worker:
|
||||||
mdns.get_serial_number.return_value = ('SUCCESS', 10, 1, )
|
get_worker.return_value = worker
|
||||||
|
worker.get_serial_number.return_value = ('SUCCESS', 10)
|
||||||
|
|
||||||
response = self.client.post_json(
|
response = self.client.post_json(
|
||||||
'/zones/%s/tasks/xfr' % zone['id'],
|
'/zones/%s/tasks/xfr' % zone['id'],
|
||||||
None, status=202)
|
None, status=202)
|
||||||
|
|
||||||
self.assertTrue(mdns.perform_zone_xfr.called)
|
self.assertTrue(worker.perform_zone_xfr.called)
|
||||||
|
|
||||||
# Check the headers are what we expect
|
# Check the headers are what we expect
|
||||||
self.assertEqual(202, response.status_int)
|
self.assertEqual(202, response.status_int)
|
||||||
|
@@ -34,12 +34,12 @@ import testtools
|
|||||||
from testtools.matchers import GreaterThan
|
from testtools.matchers import GreaterThan
|
||||||
|
|
||||||
from designate import exceptions
|
from designate import exceptions
|
||||||
from designate.mdns import rpcapi as mdns_api
|
|
||||||
from designate import objects
|
from designate import objects
|
||||||
from designate.storage.impl_sqlalchemy import tables
|
from designate.storage.impl_sqlalchemy import tables
|
||||||
from designate.tests import fixtures
|
from designate.tests import fixtures
|
||||||
from designate.tests.test_central import CentralTestCase
|
from designate.tests.test_central import CentralTestCase
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate.worker import rpcapi as worker_api
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -1307,13 +1307,15 @@ class CentralServiceTest(CentralTestCase):
|
|||||||
# Create a zone
|
# Create a zone
|
||||||
secondary = self.create_zone(**fixture)
|
secondary = self.create_zone(**fixture)
|
||||||
|
|
||||||
mdns = mock.Mock()
|
worker = mock.Mock()
|
||||||
with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
|
with mock.patch.object(worker_api.WorkerAPI,
|
||||||
get_mdns.return_value = mdns
|
'get_instance') as get_worker:
|
||||||
mdns.get_serial_number.return_value = ('SUCCESS', 10, 1, )
|
get_worker.return_value = worker
|
||||||
|
worker.get_serial_number.return_value = ('SUCCESS', 10)
|
||||||
|
|
||||||
self.central_service.xfr_zone(self.admin_context, secondary.id)
|
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):
|
def test_xfr_zone_same_serial(self):
|
||||||
# Create a zone
|
# Create a zone
|
||||||
@@ -1324,13 +1326,14 @@ class CentralServiceTest(CentralTestCase):
|
|||||||
# Create a zone
|
# Create a zone
|
||||||
secondary = self.create_zone(**fixture)
|
secondary = self.create_zone(**fixture)
|
||||||
|
|
||||||
mdns = mock.Mock()
|
worker = mock.Mock()
|
||||||
with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
|
with mock.patch.object(worker_api.WorkerAPI,
|
||||||
get_mdns.return_value = mdns
|
'get_instance') as get_worker:
|
||||||
mdns.get_serial_number.return_value = ('SUCCESS', 1, 1, )
|
get_worker.return_value = worker
|
||||||
|
worker.get_serial_number.return_value = ('SUCCESS', 1)
|
||||||
self.central_service.xfr_zone(self.admin_context, secondary.id)
|
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):
|
def test_xfr_zone_lower_serial(self):
|
||||||
# Create a zone
|
# Create a zone
|
||||||
@@ -1343,13 +1346,14 @@ class CentralServiceTest(CentralTestCase):
|
|||||||
secondary = self.create_zone(**fixture)
|
secondary = self.create_zone(**fixture)
|
||||||
secondary.serial
|
secondary.serial
|
||||||
|
|
||||||
mdns = mock.Mock()
|
worker = mock.Mock()
|
||||||
with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
|
with mock.patch.object(worker_api.WorkerAPI,
|
||||||
get_mdns.return_value = mdns
|
'get_instance') as get_worker:
|
||||||
mdns.get_serial_number.return_value = ('SUCCESS', 0, 1, )
|
get_worker.return_value = worker
|
||||||
|
worker.get_serial_number.return_value = ('SUCCESS', 0)
|
||||||
self.central_service.xfr_zone(self.admin_context, secondary.id)
|
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):
|
def test_xfr_zone_invalid_type(self):
|
||||||
zone = self.create_zone()
|
zone = self.create_zone()
|
||||||
|
@@ -177,9 +177,6 @@ class MdnsRequestHandlerTest(MdnsTestCase):
|
|||||||
return_value=zone):
|
return_value=zone):
|
||||||
response = next(self.handler(request)).to_wire()
|
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))
|
self.assertEqual(expected_response, binascii.b2a_hex(response))
|
||||||
|
|
||||||
@mock.patch.object(dns.resolver.Resolver, 'query')
|
@mock.patch.object(dns.resolver.Resolver, 'query')
|
||||||
|
@@ -21,13 +21,12 @@ import dns.exception
|
|||||||
import dns.message
|
import dns.message
|
||||||
import dns.query
|
import dns.query
|
||||||
|
|
||||||
from designate.mdns import notify
|
|
||||||
from designate import objects
|
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 = {
|
test_zone = {
|
||||||
'name': 'example.com.',
|
'name': 'example.com.',
|
||||||
'email': 'example@example.com',
|
'email': 'example@example.com',
|
||||||
@@ -35,14 +34,13 @@ class MdnsNotifyTest(MdnsTestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(MdnsNotifyTest, self).setUp()
|
super(WorkerNotifyTest, self).setUp()
|
||||||
self.nameserver = objects.PoolNameserver.from_dict({
|
self.nameserver = objects.PoolNameserver.from_dict({
|
||||||
'id': 'f278782a-07dc-4502-9177-b5d85c5f7c7e',
|
'id': 'f278782a-07dc-4502-9177-b5d85c5f7c7e',
|
||||||
'host': '127.0.0.1',
|
'host': '127.0.0.1',
|
||||||
'port': 65255
|
'port': 65255
|
||||||
})
|
})
|
||||||
self.mock_tg = mock.Mock()
|
self.mock_tg = mock.Mock()
|
||||||
self.notify = notify.NotifyEndpoint(self.mock_tg)
|
|
||||||
|
|
||||||
def test_poll_for_serial_number(self):
|
def test_poll_for_serial_number(self):
|
||||||
# id 10001
|
# id 10001
|
||||||
@@ -62,12 +60,14 @@ class MdnsNotifyTest(MdnsTestCase):
|
|||||||
"00000e10")
|
"00000e10")
|
||||||
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
||||||
binascii.a2b_hex(poll_response))):
|
binascii.a2b_hex(poll_response))):
|
||||||
status, serial, retries = self.notify.get_serial_number(
|
get_zone_serial = zone.GetZoneSerial(
|
||||||
'context', objects.Zone.from_dict(self.test_zone),
|
self.mock_tg, 'context',
|
||||||
self.nameserver.host, self.nameserver.port, 0, 0, 2, 0)
|
objects.Zone.from_dict(self.test_zone),
|
||||||
self.assertEqual(status, 'SUCCESS')
|
self.nameserver.host, self.nameserver.port,
|
||||||
self.assertEqual(serial, self.test_zone['serial'])
|
)
|
||||||
self.assertEqual(retries, 2)
|
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):
|
def test_poll_for_serial_number_lower_serial(self):
|
||||||
# id 10001
|
# id 10001
|
||||||
@@ -87,12 +87,14 @@ class MdnsNotifyTest(MdnsTestCase):
|
|||||||
"00000e10")
|
"00000e10")
|
||||||
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
||||||
binascii.a2b_hex(poll_response))):
|
binascii.a2b_hex(poll_response))):
|
||||||
status, serial, retries = self.notify.get_serial_number(
|
get_zone_serial = zone.GetZoneSerial(
|
||||||
'context', objects.Zone.from_dict(self.test_zone),
|
self.mock_tg, 'context',
|
||||||
self.nameserver.host, self.nameserver.port, 0, 0, 2, 0)
|
objects.Zone.from_dict(self.test_zone),
|
||||||
self.assertEqual(status, 'ERROR')
|
self.nameserver.host, self.nameserver.port,
|
||||||
self.assertEqual(serial, 99)
|
)
|
||||||
self.assertEqual(retries, 0)
|
result = get_zone_serial()
|
||||||
|
self.assertEqual(result[0], 'SUCCESS')
|
||||||
|
self.assertEqual(result[1], 99)
|
||||||
|
|
||||||
def test_poll_for_serial_number_higher_serial(self):
|
def test_poll_for_serial_number_higher_serial(self):
|
||||||
# id 10001
|
# id 10001
|
||||||
@@ -112,18 +114,23 @@ class MdnsNotifyTest(MdnsTestCase):
|
|||||||
"00000e10")
|
"00000e10")
|
||||||
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
with patch.object(dns.query, 'udp', return_value=dns.message.from_wire(
|
||||||
binascii.a2b_hex(poll_response))):
|
binascii.a2b_hex(poll_response))):
|
||||||
status, serial, retries = self.notify.get_serial_number(
|
get_zone_serial = zone.GetZoneSerial(
|
||||||
'context', objects.Zone.from_dict(self.test_zone),
|
self.mock_tg, 'context',
|
||||||
self.nameserver.host, self.nameserver.port, 0, 0, 2, 0)
|
objects.Zone.from_dict(self.test_zone),
|
||||||
self.assertEqual(status, 'SUCCESS')
|
self.nameserver.host, self.nameserver.port,
|
||||||
self.assertEqual(serial, 101)
|
)
|
||||||
self.assertEqual(retries, 2)
|
result = get_zone_serial()
|
||||||
|
self.assertEqual(result[0], 'SUCCESS')
|
||||||
|
self.assertEqual(result[1], 101)
|
||||||
|
|
||||||
@patch.object(dns.query, 'udp', side_effect=dns.exception.Timeout)
|
@patch.object(dns.query, 'udp', side_effect=dns.exception.Timeout)
|
||||||
def test_poll_for_serial_number_timeout(self, _):
|
def test_poll_for_serial_number_timeout(self, _):
|
||||||
status, serial, retries = self.notify.get_serial_number(
|
self.CONF.set_override('serial_timeout', 1, 'service:worker')
|
||||||
'context', objects.Zone.from_dict(self.test_zone),
|
get_zone_serial = zone.GetZoneSerial(
|
||||||
self.nameserver.host, self.nameserver.port, 0, 0, 2, 0)
|
self.mock_tg, 'context',
|
||||||
self.assertEqual(status, 'ERROR')
|
objects.Zone.from_dict(self.test_zone),
|
||||||
self.assertIsNone(serial)
|
self.nameserver.host, self.nameserver.port,
|
||||||
self.assertEqual(retries, 0)
|
)
|
||||||
|
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.mdns import handler
|
||||||
from designate import objects
|
from designate import objects
|
||||||
from designate.tests import fixtures
|
from designate.tests import fixtures
|
||||||
|
from designate.worker import rpcapi as worker_rpcapi
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
@@ -39,6 +40,14 @@ class MdnsHandleTest(oslotest.base.BaseTestCase):
|
|||||||
self.tg = mock.Mock()
|
self.tg = mock.Mock()
|
||||||
self.handler = handler.RequestHandler(self.storage, self.tg)
|
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')
|
@mock.patch.object(dns.resolver.Resolver, 'query')
|
||||||
def test_notify(self, mock_query):
|
def test_notify(self, mock_query):
|
||||||
self.storage.find_zone.return_value = objects.Zone(
|
self.storage.find_zone.return_value = objects.Zone(
|
||||||
@@ -206,7 +215,6 @@ class TestRequestHandlerCall(oslotest.base.BaseTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestRequestHandlerCall, self).setUp()
|
super(TestRequestHandlerCall, self).setUp()
|
||||||
self.handler = handler.RequestHandler(mock.Mock(), mock.Mock())
|
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
|
# Use a simple handlers that doesn't require a real request
|
||||||
self.handler._handle_query_error = mock.Mock(return_value='Error')
|
self.handler._handle_query_error = mock.Mock(return_value='Error')
|
||||||
@@ -215,10 +223,6 @@ class TestRequestHandlerCall(oslotest.base.BaseTestCase):
|
|||||||
return_value=['Record Query'])
|
return_value=['Record Query'])
|
||||||
self.handler._handle_notify = mock.Mock(return_value=['Notify'])
|
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):
|
def test__call___unhandled_opcodes(self):
|
||||||
unhandled_codes = [
|
unhandled_codes = [
|
||||||
dns.opcode.STATUS,
|
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()
|
self.service = service.Service()
|
||||||
|
|
||||||
@mock.patch.object(designate.service.DNSService, 'start')
|
@mock.patch.object(designate.service.DNSService, 'start')
|
||||||
@mock.patch.object(designate.service.RPCService, 'start')
|
def test_service_start(self, mock_dns_start):
|
||||||
def test_service_start(self, mock_rpc_start, mock_dns_start):
|
|
||||||
self.service.start()
|
self.service.start()
|
||||||
|
|
||||||
self.assertTrue(mock_dns_start.called)
|
self.assertTrue(mock_dns_start.called)
|
||||||
self.assertTrue(mock_rpc_start.called)
|
|
||||||
|
|
||||||
def test_service_stop(self):
|
def test_service_stop(self):
|
||||||
self.service.dns_service.stop = mock.Mock()
|
self.service.dns_service.stop = mock.Mock()
|
||||||
@@ -63,14 +61,6 @@ class MdnsServiceTest(oslotest.base.BaseTestCase):
|
|||||||
def test_service_name(self):
|
def test_service_name(self):
|
||||||
self.assertEqual('mdns', self.service.service_name)
|
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')
|
@mock.patch.object(storage, 'get_storage')
|
||||||
def test_storage_driver(self, mock_get_driver):
|
def test_storage_driver(self, mock_get_driver):
|
||||||
self.service._storage = None
|
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
|
# 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(
|
fx_worker = fixtures.MockPatch(
|
||||||
'designate.central.service.worker_rpcapi.WorkerAPI.get_instance',
|
'designate.central.service.worker_rpcapi.WorkerAPI.get_instance',
|
||||||
mock.MagicMock(spec_set=[
|
mock.MagicMock(spec_set=[
|
||||||
@@ -281,12 +272,6 @@ class CentralBasic(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class CentralServiceTestCase(CentralBasic):
|
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):
|
def test_conf_fixture(self):
|
||||||
assert 'service:central' in designate.central.service.cfg.CONF
|
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)],
|
masters=[RoObject(host='10.0.0.1', port=53)],
|
||||||
serial=1,
|
serial=1,
|
||||||
)
|
)
|
||||||
with fx_mdns_api:
|
with fx_worker:
|
||||||
self.service.mdns_api.get_serial_number.return_value = \
|
self.service.worker_api.get_serial_number.return_value = (
|
||||||
"SUCCESS", 2, 1
|
'SUCCESS', 2
|
||||||
|
)
|
||||||
self.service.xfr_zone(
|
self.service.xfr_zone(
|
||||||
self.context, CentralZoneTestCase.zone__id)
|
self.context, CentralZoneTestCase.zone__id)
|
||||||
self.assertTrue(
|
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.assertTrue(designate.central.service.policy.check.called)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@@ -24,6 +24,7 @@ import dns.rdatatype
|
|||||||
import dns.zone
|
import dns.zone
|
||||||
import eventlet
|
import eventlet
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_config import fixture as cfg_fixture
|
||||||
import oslotest.base
|
import oslotest.base
|
||||||
|
|
||||||
from designate import dnsutils
|
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
|
# This needs to be a one item tuple for the serialization middleware
|
||||||
self.assertEqual(middleware.process_request(notify), (response,))
|
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):
|
class TestDoAfxr(oslotest.base.BaseTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestDoAfxr, self).setUp()
|
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.query, 'xfr')
|
||||||
@mock.patch.object(dns.zone, 'from_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())
|
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:
|
API version history:
|
||||||
|
|
||||||
1.0 - Initial version
|
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):
|
def __init__(self, topic=None):
|
||||||
self.topic = topic if topic else cfg.CONF['service:worker'].topic
|
self.topic = topic if topic else cfg.CONF['service:worker'].topic
|
||||||
|
|
||||||
target = messaging.Target(topic=self.topic,
|
target = messaging.Target(topic=self.topic,
|
||||||
version=self.RPC_API_VERSION)
|
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
|
@classmethod
|
||||||
def get_instance(cls):
|
def get_instance(cls):
|
||||||
@@ -78,3 +79,13 @@ class WorkerAPI(object):
|
|||||||
def start_zone_export(self, context, zone, export):
|
def start_zone_export(self, context, zone, export):
|
||||||
return self.client.cast(
|
return self.client.cast(
|
||||||
context, 'start_zone_export', zone=zone, export=export)
|
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):
|
class Service(service.RPCService):
|
||||||
RPC_API_VERSION = '1.0'
|
RPC_API_VERSION = '1.1'
|
||||||
|
|
||||||
target = messaging.Target(version=RPC_API_VERSION)
|
target = messaging.Target(version=RPC_API_VERSION)
|
||||||
|
|
||||||
@@ -143,10 +143,10 @@ class Service(service.RPCService):
|
|||||||
|
|
||||||
def _do_zone_action(self, context, zone):
|
def _do_zone_action(self, context, zone):
|
||||||
pool = self.get_pool(zone.pool_id)
|
pool = self.get_pool(zone.pool_id)
|
||||||
all_tasks = []
|
all_tasks = [
|
||||||
all_tasks.append(zonetasks.ZoneAction(
|
zonetasks.ZoneAction(self.executor, context, pool, zone,
|
||||||
self.executor, context, pool, zone, zone.action
|
zone.action)
|
||||||
))
|
]
|
||||||
|
|
||||||
# Send a NOTIFY to each also-notifies
|
# Send a NOTIFY to each also-notifies
|
||||||
for also_notify in pool.also_notifies:
|
for also_notify in pool.also_notifies:
|
||||||
@@ -206,3 +206,26 @@ class Service(service.RPCService):
|
|||||||
return self.executor.run(zonetasks.ExportZone(
|
return self.executor.run(zonetasks.ExportZone(
|
||||||
self.executor, context, zone, export
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
import errno
|
||||||
|
import socket
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import dns
|
import dns
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import timeutils
|
||||||
|
|
||||||
from designate import dnsutils
|
from designate import dnsutils
|
||||||
from designate import exceptions
|
from designate import exceptions
|
||||||
|
from designate import objects
|
||||||
from designate import utils
|
from designate import utils
|
||||||
from designate.worker.tasks import base
|
from designate.worker.tasks import base
|
||||||
|
|
||||||
@@ -150,6 +154,36 @@ class SendNotify(base.Task):
|
|||||||
return False
|
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):
|
class ZoneActor(base.Task):
|
||||||
"""
|
"""
|
||||||
Orchestrate the Create/Update/Delete action on targets and update status
|
Orchestrate the Create/Update/Delete action on targets and update status
|
||||||
@@ -558,6 +592,136 @@ class ZonePoller(base.Task):
|
|||||||
return result
|
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
|
# Status Management
|
||||||
###################
|
###################
|
||||||
|
@@ -104,8 +104,6 @@ How do I monitor Designate?
|
|||||||
Designate can be monitored by various
|
Designate can be monitored by various
|
||||||
`monitoring systems listed here <https://wiki.openstack.org/wiki/Operations/Monitoring>`_
|
`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?
|
What are useful metrics to monitor?
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
@@ -16,7 +16,6 @@ Contents:
|
|||||||
Designate Tempest Plugin <https://docs.openstack.org/designate-tempest-plugin/latest>
|
Designate Tempest Plugin <https://docs.openstack.org/designate-tempest-plugin/latest>
|
||||||
architecture
|
architecture
|
||||||
gmr
|
gmr
|
||||||
metrics
|
|
||||||
sourcedoc/index
|
sourcedoc/index
|
||||||
ubuntu-dev
|
ubuntu-dev
|
||||||
integrations
|
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
|
||||||
****
|
****
|
||||||
|
|
||||||
MDNS Base
|
|
||||||
=========
|
|
||||||
.. automodule:: designate.mdns.base
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
MDNS Handler
|
MDNS Handler
|
||||||
============
|
============
|
||||||
.. automodule:: designate.mdns.handler
|
.. automodule:: designate.mdns.handler
|
||||||
@@ -18,19 +11,6 @@ MDNS Handler
|
|||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
: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
|
MDNS Service
|
||||||
============
|
============
|
||||||
@@ -38,10 +18,3 @@ MDNS Service
|
|||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
: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
|
tooz>=1.58.0 # Apache-2.0
|
||||||
debtcollector>=1.19.0 # Apache-2.0
|
debtcollector>=1.19.0 # Apache-2.0
|
||||||
os-win>=4.1.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
|
futurist>=1.2.0 # Apache-2.0
|
||||||
edgegrid-python>=1.1.1 # Apache-2.0
|
edgegrid-python>=1.1.1 # Apache-2.0
|
||||||
|
Reference in New Issue
Block a user