Merge "Support multiple API and mDNS listen address pairs"

This commit is contained in:
Jenkins 2016-05-04 17:22:11 +00:00 committed by Gerrit Code Review
commit 20b5c13fca
10 changed files with 141 additions and 56 deletions

View File

@ -24,10 +24,17 @@ OPTS = [
help='Number of agent worker processes to spawn'),
cfg.IntOpt('threads', default=1000,
help='Number of agent greenthreads to spawn'),
cfg.IPOpt('host', default='0.0.0.0',
help='The host for the Agent to bind to'),
cfg.PortOpt('port', default=5358,
help='The port for the Agent to bind to'),
cfg.IPOpt('host',
deprecated_for_removal=True,
deprecated_reason="Replaced by 'listen' option",
help='Agent Bind Host'),
cfg.PortOpt('port',
deprecated_for_removal=True,
deprecated_reason="Replaced by 'listen' option",
help='Agent Port Number'),
cfg.ListOpt('listen',
default=['0.0.0.0:5358'],
help='Agent host:port pairs to listen on'),
cfg.IntOpt('tcp-backlog', default=100,
help='The Agent TCP Backlog'),
cfg.FloatOpt('tcp-recv-timeout', default=0.5,

View File

@ -37,6 +37,7 @@ CONF = cfg.CONF
class Service(service.DNSService, service.Service):
_dns_default_port = 5358
def __init__(self, threads=None):
super(Service, self).__init__(threads=threads)

View File

@ -28,10 +28,17 @@ cfg.CONF.register_opts([
cfg.BoolOpt('enable-host-header', default=False,
help='Enable host request headers'),
cfg.StrOpt('api-base-uri', default='http://127.0.0.1:9001/'),
cfg.IPOpt('api_host', default='0.0.0.0',
help='API Host'),
cfg.PortOpt('api_port', default=9001,
cfg.IPOpt('api_host',
deprecated_for_removal=True,
deprecated_reason="Replaced by 'listen' option",
help='API Bind Host'),
cfg.PortOpt('api_port',
deprecated_for_removal=True,
deprecated_reason="Replaced by 'listen' option",
help='API Port Number'),
cfg.ListOpt('listen',
default=['0.0.0.0:9001'],
help='API host:port pairs to listen on'),
cfg.StrOpt('api_paste_config', default='api-paste.ini',
help='File name for the paste.deploy config for designate-api'),
cfg.StrOpt('auth_strategy', default='keystone',

View File

@ -27,10 +27,17 @@ OPTS = [
help='Number of mdns worker processes to spawn'),
cfg.IntOpt('threads', default=1000,
help='Number of mdns greenthreads to spawn'),
cfg.IPOpt('host', default='0.0.0.0',
cfg.IPOpt('host',
deprecated_for_removal=True,
deprecated_reason="Replaced by 'listen' option",
help='mDNS Bind Host'),
cfg.PortOpt('port', default=5354,
cfg.PortOpt('port',
deprecated_for_removal=True,
deprecated_reason="Replaced by 'listen' option",
help='mDNS Port Number'),
cfg.ListOpt('listen',
default=['0.0.0.0:5354'],
help='mDNS host:port pairs to listen on'),
cfg.IntOpt('tcp-backlog', default=100,
help='mDNS TCP Backlog'),
cfg.FloatOpt('tcp-recv-timeout', default=0.5,

View File

@ -29,6 +29,7 @@ CONF = cfg.CONF
class Service(service.DNSService, service.RPCService, service.Service):
_dns_default_port = 5354
@property
def storage(self):

View File

@ -97,6 +97,43 @@ class Service(service.Service):
super(Service, self).stop()
def _get_listen_on_addresses(self, default_port):
"""
Helper Method to handle migration from singular host/port to
multiple binds
"""
try:
# The API service uses "api_host", and "api_port", others use
# just host and port.
host = self._service_config.api_host
port = self._service_config.api_port
except cfg.NoSuchOptError:
host = self._service_config.host
port = self._service_config.port
if host or port:
LOG.warning(_LW("host and port config options used, the 'listen' "
"option has been ignored"))
host = host or "0.0.0.0"
port = port or default_port
return [(host, port)]
else:
def _split_host_port(l):
try:
host, port = l.split(':', 1)
return host, int(port)
except ValueError:
LOG.exception(_LE('Invalid ip:port pair: %s'), l)
raise
# Convert listen pair list to a set, to remove accidental
# duplicates.
return map(_split_host_port, set(self._service_config.listen))
class RPCService(object):
"""
@ -180,6 +217,8 @@ class WSGIService(object):
def __init__(self, *args, **kwargs):
super(WSGIService, self).__init__(*args, **kwargs)
self._wsgi_socks = []
@abc.abstractproperty
def _wsgi_application(self):
pass
@ -187,23 +226,28 @@ class WSGIService(object):
def start(self):
super(WSGIService, self).start()
self._wsgi_sock = utils.bind_tcp(
self._service_config.api_host,
self._service_config.api_port,
CONF.backlog,
CONF.tcp_keepidle)
addresses = self._get_listen_on_addresses(9001)
for address in addresses:
self._start(address[0], address[1])
def _start(self, host, port):
wsgi_sock = utils.bind_tcp(
host, port, CONF.backlog, CONF.tcp_keepidle)
if sslutils.is_enabled(CONF):
self._wsgi_sock = sslutils.wrap(CONF, self._wsgi_sock)
wsgi_sock = sslutils.wrap(CONF, wsgi_sock)
self.tg.add_thread(self._wsgi_handle)
self._wsgi_socks.append(wsgi_sock)
def _wsgi_handle(self):
self.tg.add_thread(self._wsgi_handle, wsgi_sock)
def _wsgi_handle(self, wsgi_sock):
logger = logging.getLogger('eventlet.wsgi')
# Adjust wsgi MAX_HEADER_LINE to accept large tokens.
eventlet.wsgi.MAX_HEADER_LINE = self._service_config.max_header_line
eventlet.wsgi.server(self._wsgi_sock,
eventlet.wsgi.server(wsgi_sock,
self._wsgi_application,
custom_pool=self.tg.pool,
log=logger)
@ -221,6 +265,9 @@ class DNSService(object):
# reading/writing to the UDP socket at once. Disable this warning.
eventlet.debug.hub_prevent_multiple_readers(False)
self._dns_socks_tcp = []
self._dns_socks_udp = []
@abc.abstractproperty
def _dns_application(self):
pass
@ -228,17 +275,23 @@ class DNSService(object):
def start(self):
super(DNSService, self).start()
self._dns_sock_tcp = utils.bind_tcp(
self._service_config.host,
self._service_config.port,
self._service_config.tcp_backlog)
addresses = self._get_listen_on_addresses(self._dns_default_port)
self._dns_sock_udp = utils.bind_udp(
self._service_config.host,
self._service_config.port)
for address in addresses:
self._start(address[0], address[1])
self.tg.add_thread(self._dns_handle_tcp)
self.tg.add_thread(self._dns_handle_udp)
def _start(self, host, port):
sock_tcp = utils.bind_tcp(
host, port, self._service_config.tcp_backlog)
sock_udp = utils.bind_udp(
host, port)
self._dns_socks_tcp.append(sock_tcp)
self._dns_socks_udp.append(sock_udp)
self.tg.add_thread(self._dns_handle_tcp, sock_tcp)
self.tg.add_thread(self._dns_handle_udp, sock_udp)
def wait(self):
super(DNSService, self).wait()
@ -248,18 +301,18 @@ class DNSService(object):
# _handle_udp are stopped too.
super(DNSService, self).stop()
if hasattr(self, '_dns_sock_tcp'):
self._dns_sock_tcp.close()
for sock_tcp in self._dns_socks_tcp:
sock_tcp.close()
if hasattr(self, '_dns_sock_udp'):
self._dns_sock_udp.close()
for sock_udp in self._dns_socks_udp:
sock_udp.close()
def _dns_handle_tcp(self):
def _dns_handle_tcp(self, sock_tcp):
LOG.info(_LI("_handle_tcp thread started"))
while True:
try:
client, addr = self._dns_sock_tcp.accept()
client, addr = sock_tcp.accept()
if self._service_config.tcp_recv_timeout:
client.settimeout(self._service_config.tcp_recv_timeout)
@ -312,20 +365,21 @@ class DNSService(object):
self.tg.add_thread(self._dns_handle, addr, payload,
client=client)
def _dns_handle_udp(self):
def _dns_handle_udp(self, sock_udp):
LOG.info(_LI("_handle_udp thread started"))
while True:
try:
# TODO(kiall): Determine the appropriate default value for
# UDP recvfrom.
payload, addr = self._dns_sock_udp.recvfrom(8192)
payload, addr = sock_udp.recvfrom(8192)
LOG.debug("Handling UDP Request from: %(host)s:%(port)d" %
{'host': addr[0], 'port': addr[1]})
# Dispatch a thread to handle the query
self.tg.add_thread(self._dns_handle, addr, payload)
self.tg.add_thread(self._dns_handle, addr, payload,
sock_udp=sock_udp)
except socket.error as e:
errname = errno.errorcode[e.args[0]]
@ -338,7 +392,7 @@ class DNSService(object):
"from: %(host)s:%(port)d") %
{'host': addr[0], 'port': addr[1]})
def _dns_handle(self, addr, payload, client=None):
def _dns_handle(self, addr, payload, client=None, sock_udp=None):
"""
Handle a DNS Query
@ -360,7 +414,7 @@ class DNSService(object):
client.sendall(tcp_response)
else:
# Handle UDP Responses
self._dns_sock_udp.sendto(response, addr)
sock_udp.sendto(response, addr)
except Exception:
LOG.exception(_LE("Unhandled exception while processing request "

View File

@ -60,6 +60,8 @@ class MdnsServiceTest(MdnsTestCase):
expected_response = (b"271289050001000000000000076578616d706c6503636f6"
b"d0000010001")
self.service._dns_handle(self.addr, binascii.a2b_hex(payload))
sock_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.service._dns_handle(self.addr, binascii.a2b_hex(payload),
sock_udp=sock_udp)
sendto_mock.assert_called_once_with(
binascii.a2b_hex(expected_response), self.addr)

View File

@ -82,15 +82,13 @@ function configure_designate {
iniset $DESIGNATE_CONF service:api enabled_extensions_v1 $DESIGNATE_ENABLED_EXTENSIONS_V1
iniset $DESIGNATE_CONF service:api enabled_extensions_v2 $DESIGNATE_ENABLED_EXTENSIONS_V2
iniset $DESIGNATE_CONF service:api enabled_extensions_admin $DESIGNATE_ENABLED_EXTENSIONS_ADMIN
iniset $DESIGNATE_CONF service:api api_host $DESIGNATE_SERVICE_HOST
iniset $DESIGNATE_CONF service:api api_base_uri $DESIGNATE_SERVICE_PROTOCOL://$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT/
iniset $DESIGNATE_CONF service:api enable_api_v1 $DESIGNATE_ENABLE_API_V1
iniset $DESIGNATE_CONF service:api enable_api_v2 $DESIGNATE_ENABLE_API_V2
iniset $DESIGNATE_CONF service:api enable_api_admin $DESIGNATE_ENABLE_API_ADMIN
# mDNS Configuration
iniset $DESIGNATE_CONF service:mdns host $DESIGNATE_SERVICE_HOST
iniset $DESIGNATE_CONF service:mdns port $DESIGNATE_SERVICE_PORT_MDNS
iniset $DESIGNATE_CONF service:mdns listen ${DESIGNATE_SERVICE_HOST}:${DESIGNATE_SERVICE_PORT_MDNS}
# Set up Notifications/Ceilometer Integration
iniset $DESIGNATE_CONF DEFAULT notification_driver "$DESIGNATE_NOTIFICATION_DRIVER"
@ -114,9 +112,9 @@ function configure_designate {
# TLS Proxy Configuration
if is_service_enabled tls-proxy; then
# Set the service port for a proxy to take the original
iniset $DESIGNATE_CONF service:api api_port $DESIGNATE_SERVICE_PORT_INT
iniset $DESIGNATE_CONF service:api listen ${DESIGNATE_SERVICE_HOST}:${DESIGNATE_SERVICE_PORT_INT}
else
iniset $DESIGNATE_CONF service:api api_port $DESIGNATE_SERVICE_PORT
iniset $DESIGNATE_CONF service:api listen ${DESIGNATE_SERVICE_HOST}:${DESIGNATE_SERVICE_PORT}
fi
# Setup the Keystone Integration

View File

@ -100,11 +100,8 @@ debug = False
# The base uri used in responses
#api_base_uri = 'http://127.0.0.1:9001/'
# Address to bind the API server
#api_host = 0.0.0.0
# Port the bind the API server to
#api_port = 9001
# API bind host+port pairs, comma separated
#listen = 0.0.0.0:9001
# Maximum line size of message headers to be accepted. max_header_line may
# need to be increased when using large tokens (typically those generated by
@ -238,11 +235,8 @@ debug = False
# Number of mdns greenthreads to spawn
#threads = 1000
# mDNS Bind Host
#host = 0.0.0.0
# mDNS Port Number
#port = 5354
# mDNS bind host+port pairs, comma separated
#listen = 0.0.0.0:5354
# mDNS TCP Backlog
#tcp_backlog = 100
@ -264,8 +258,7 @@ debug = False
#-----------------------
[service:agent]
#workers = None
#host = 0.0.0.0
#port = 5358
#listen = 0.0.0.0:5358
#tcp_backlog = 100
#allow_notify = 127.0.0.1
#masters = 127.0.0.1:5354

View File

@ -0,0 +1,15 @@
---
features:
- designate-mdns, designate-agent and designate-api can now bind to multiple
host:port pairs via the new "listen" configuration arguments for eacg
service.
deprecations:
- designate-api's api_host and api_port configuration options have been
deprecated, please use the new combined "listen" argument in place of
these.
- designate-mdns's host and port configuration options have been
deprecated, please use the new combined "listen" argument in place of
these.
- designate-agents's host and port configuration options have been
deprecated, please use the new combined "listen" argument in place of
these.