Merge "nsd4slave backend"

This commit is contained in:
Jenkins 2013-11-20 20:51:45 +00:00 committed by Gerrit Code Review
commit 110a27c436
9 changed files with 330 additions and 0 deletions

View File

@ -0,0 +1,124 @@
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Artom Lifshitz <artom.lifshitz@enovance.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 eventlet
import socket
import ssl
from designate import exceptions
from designate.backend import base
from designate.openstack.common import log as logging
from oslo.config import cfg
LOG = logging.getLogger(__name__)
CFG_GRP = 'backend:nsd4slave'
cfg.CONF.register_group(
cfg.OptGroup(name=CFG_GRP, title='Configuration for NSD4-slave backend')
)
cfg.CONF.register_opts([
cfg.StrOpt('keyfile', default='/etc/nsd/nsd_control.key',
help='Keyfile to use when connecting to the NSD4 servers over '
'SSL'),
cfg.StrOpt('certfile', default='/etc/nsd/nsd_control.pem',
help='Certfile to use when connecting to the NSD4 servers over '
'SSL'),
cfg.ListOpt('servers',
help='Comma-separated list of servers to control, in '
' <host>:<port> format. If <port> is omitted, '
' the default 8952 is used.'),
cfg.StrOpt('pattern', default='slave',
help='Pattern to use when creating zones on the NSD4 servers. '
'This pattern must be identically configured on all NSD4 '
'servers.'),
], group=CFG_GRP)
DEFAULT_PORT = 8952
class NSD4SlaveBackend(base.Backend):
__plugin__name__ = 'nsd4slave'
NSDCT_VERSION = 'NSDCT1'
def __init__(self, central_service):
self._keyfile = cfg.CONF[CFG_GRP].keyfile
self._certfile = cfg.CONF[CFG_GRP].certfile
self._pattern = cfg.CONF[CFG_GRP].pattern
try:
self._servers = [self._parse_server(cfg_server)
for cfg_server in cfg.CONF[CFG_GRP].servers]
except TypeError:
raise exceptions.ConfigurationError('No NSD4 servers defined')
def _parse_server(self, cfg_server):
try:
(host, port) = cfg_server.split(':')
port = int(port)
except ValueError:
host = str(cfg_server)
port = DEFAULT_PORT
return {'host': host, 'port': port}
def create_domain(self, context, domain):
command = 'addzone %s %s' % (domain['name'], self._pattern)
self._all_servers(command)
def update_domain(self, context, domain):
pass
def delete_domain(self, context, domain):
command = 'delzone %s' % domain['name']
self._all_servers(command)
def _all_servers(self, command):
for server in self._servers:
try:
result = self._command(command, server['host'], server['port'])
except (ssl.SSLError, socket.error) as e:
raise exceptions.NSD4SlaveBackendError(e)
if result != 'ok':
raise exceptions.NSD4SlaveBackendError(result)
def _command(self, command, host, port):
sock = eventlet.wrap_ssl(eventlet.connect((host, port)),
keyfile=self._keyfile,
certfile=self._certfile)
stream = sock.makefile()
stream.write('%s %s\n' % (self.NSDCT_VERSION, command))
stream.flush()
result = stream.read()
stream.close()
sock.close()
return result.rstrip()
def create_record(self, context, domain, record):
pass
def update_record(self, context, domain, record):
pass
def delete_record(self, context, domain, record):
pass
def create_server(self, context, server):
pass
def update_server(self, context, server):
pass
def delete_server(self, context, server):
pass

View File

@ -34,6 +34,10 @@ class Backend(Exception):
pass
class NSD4SlaveBackendError(Backend):
pass
class NotImplemented(Base, NotImplementedError):
pass

View File

@ -0,0 +1,21 @@
-----BEGIN RSA PRIVATE KEY-----
MIIDfQIBAAKBwQClNN7DMvEDNh9g6KCpmBCT/liwH1CyUjwG2fEjrRaRkWztjmrK
Rfjt8xYukze1OFlaiVmOFaW0Mg5dJ8VWpusah8DzhL5sjg547ZbSfZhe/yA26hA/
7P2MBd9Zv9w32ErYQkJzyvgKBmivU1lyGIoVEf4jDr5rGZgz9mh5hiFPfyvnV2NW
3gk22JhEltP1XB6ERzKCMlpUAL62wErOiMUJX8YBMNoZ3jgYcjGFJENqYr6BkLz5
w5PJCGz1lN656yECAwEAAQKBwD9B3UIU0WAPazhqGoqVpVNlgoP9maKOBloBPWoR
rqCSdIkJjS5iWIyUFZxD1oLXTq9yBA55j3dN614UCmXBiCgibulPnLJoOnZnUubK
HHvTaHxeZ1Zy4iqpXPo6kpH3iSNbw1UtYJXYn9xy8lZdeMoNxMkRYSVesluIPVaA
69ro33kYUSFLRR8YQMOiHSGkOGkLu4U+7nrkbkbQjoaTv5qZquHFCnLlBpM7adYM
981fDP1dgie5qk4ND1c5nQ4AAQJhANcGNlu3yJdeK+jfpKcuHCO2+j40p9hBoZFd
oCCBjw7Q9vn4RTnVjMSGYTdBhCnLoeIuCeHySOKVf0UMOGRoOkMOUVC3U0IaOGMM
GEFDMFEEGrUbEB1n62Oq9dKMOYy3IQJhAMSwVsKuEa/6I6CdtWQv6UkVfh8aAm2M
Slj0jDYv0pX/5ciBRTsAYbwliIYHtdBoFns/vEuCxO66A1LIr5w8GuZhEdIe2jDf
Q1fErWs6mxAouw3eFW93sQzClxsbiSq0AQJgZR2rxFfZwazsUzeQc3nQi88JQOV8
JMtAUl7H5WFnx9zmt8hTrY3KA8T5xIVPxGPZPcbOqO1J7xvEXNERV85Xz57VCHMd
eRFhgNp4MYCKIR8f/Bi87EcpP5ZuwVNl4NFhAmEAh8nqC/tasLAzeoaGnUVKfC8/
ZD9zn6e0CFfEmQBJFU+WludQIVyxHNCYwVd/WQMTSkGFQGhmhx2af8OXIXiJZbVs
NTEhl437kxNwWlAaj3xfL0K8b7klXVbWNVu8ReQBAmEAj2o92RFlyTLbvfeXsGAs
Q5UTFDIzAS9MVMEp65HD8w6dZ02ruBre0KsIJs/6aEeGORH88IUrEKPPb0C12a0e
kftUBf5JemuDKFV/L5CGpaQyFA4HSjhzlSF5QahRU3oF
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICGzCCAUQCCQDPu3EY2nVlnDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDEwNu
c2QwHhcNMTMxMTE1MTkwMjA1WhcNMzMwODAyMTkwMjA1WjAWMRQwEgYDVQQDEwtu
c2QtY29udHJvbDCB3zANBgkqhkiG9w0BAQEFAAOBzQAwgckCgcEApTTewzLxAzYf
YOigqZgQk/5YsB9QslI8BtnxI60WkZFs7Y5qykX47fMWLpM3tThZWolZjhWltDIO
XSfFVqbrGofA84S+bI4OeO2W0n2YXv8gNuoQP+z9jAXfWb/cN9hK2EJCc8r4CgZo
r1NZchiKFRH+Iw6+axmYM/ZoeYYhT38r51djVt4JNtiYRJbT9VwehEcygjJaVAC+
tsBKzojFCV/GATDaGd44GHIxhSRDamK+gZC8+cOTyQhs9ZTeueshAgMBAAEwDQYJ
KoZIhvcNAQELBQADgcEAtuHD0nCV7atGnipcukBpgydxwWDN7/4Mwcw1EAOQ8B2L
Z4ckFRKXzgtlkg98FgvEcELBUK6TqrpEhYs0ADfPogyFhcwozqmngFHXY21caHgL
FA56nebJysDH+AoyMEJHpEDqPZA/ZIwB7XsmPFs71USiqnihhfUGA01TxF1P853m
yd7iIx926gCHvzbReJBbnxdnzcjon4m13/O8y7sBGCRiw/orKUe4CW6ubAKfwpQF
qGDccgTl9x9R6EGRW5bx
-----END CERTIFICATE-----

View File

@ -0,0 +1,21 @@
-----BEGIN RSA PRIVATE KEY-----
MIIDfQIBAAKBwQDIAkIPjr2dajz/LwViY9w7OgT4eR8FhL/KwTjh4oMHpUosMyJl
12nyp/3ylCjJLJAcTfsmDUhvQKw5o+0hUOv/FBfJ43hotdn1YLq0iD+UUhzhmavV
baMM0shPrRGYDf/mHkGDkS11qsXRrUlxWr1/qjUf6ss/CbnvwQtyL/76TAtNl5Zi
MQCqrGbXmA6NOgpH51W048FD6hg0KlXEAgKV2dm+wRMNSY0Wy7b3di21bhuvst2r
5MXFxqsXTWK65qcCAwEAAQKBwQCyo8mrKZvUSHpYlf/iJD0lLSoZX91ESZAgITqU
DoNnxcsc9UMI4UEA+ejIzGotXL8OeNwT3ZNPwtzQ0shOlH9koeapbKE0LRCSqKW5
72OSL1Eacu3WQAl8v5soBvWK8RyFQCThWG9KJexh7aGjEakeMrFBSOz7PcMAY71f
2sher/Yk71DqbB/GwuBZZ5VIr3IoDrhz35URJtvzDfJ1KdIwUbKb3b54Rn61qx/i
MQiqC1EA8EwqnUtNVObJKx5L7hkCYQDrSddKhAK02zHYCH8ah1mx6qjiKxWHRNiX
PnSo4Z58jGg3ygbOTRXh+EbwbGwhF1uqdD1EQ8+uHOqaS8Nmscr/quW0zGQFMDzu
55xmmXxBQmul+6TUmT1YErENSbsLB2UCYQDZnWaU/j30VoKDO4F8zbHe1O6Yinq5
P5gIbLJQi7RxIlQ/8YvYCqgCuSzr6nrqdH83vbsGWGqIvQExgrSELLQCwB8fqOGv
U6M0+0NeaQmyU4Q3AGF0CH4eGLkY8Pg4MxsCYF1dodbp54EI8hB93qIST59wNK/6
1MImqaPqnrRdQ1y9AqYQvv6iTCqtMMk63PWMpU1QbvdlyUWYJ+guZE2eA6XMlPZX
nOEKMUPEGKYGBe6HgtwMrW3HmTYXxLY3KcfImQJgIagObYzE2D1pAhL5++t0Txpv
rHf+cxg601K8YWi6B6VfkmQxVMCRK5qoL/Sb/hb2dhCKFHkoQO26eYXVlXu0e5hr
N+JOxWcSHuedi8SDE2mHUVpluCR4HP+F4S2jtk1xAmEAoii5Rghoh/MMzHdn2loE
pTZAy0mM+Iw5Cba3QXfGA1wrM/Kyg/nJm8LYZqiRzR8smOiM3TL7+qBtN2LA36Pb
n4ryn2L3f5dBOYYuESUpmy/HHsiuzZU3Ljt333tse9cg
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICEzCCATwCCQDmQwk4gt2rdDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDEwNu
c2QwHhcNMTMxMTE1MTkwMjA1WhcNMzMwODAyMTkwMjA1WjAOMQwwCgYDVQQDEwNu
c2Qwgd8wDQYJKoZIhvcNAQEBBQADgc0AMIHJAoHBAMgCQg+OvZ1qPP8vBWJj3Ds6
BPh5HwWEv8rBOOHigwelSiwzImXXafKn/fKUKMkskBxN+yYNSG9ArDmj7SFQ6/8U
F8njeGi12fVgurSIP5RSHOGZq9VtowzSyE+tEZgN/+YeQYORLXWqxdGtSXFavX+q
NR/qyz8Jue/BC3Iv/vpMC02XlmIxAKqsZteYDo06CkfnVbTjwUPqGDQqVcQCApXZ
2b7BEw1JjRbLtvd2LbVuG6+y3avkxcXGqxdNYrrmpwIDAQABMA0GCSqGSIb3DQEB
CwUAA4HBAFKKQBBSAZXQaHTdRl/MuBOzIqCkzOIk0igTOLBYdwGO/xx2moo77CgD
VsYS5gHl4w211d0B8IMQKWKcNfjiPJyAOCdMGS/8NAK3Bz1umLHQn/C8ScOB0Xzc
kwUdewVTNo+7kEu8HgkJhlkXgP/PKSseZ+R8VD3ozlFQS8uojmQjzMs5ne1zkph2
KiRTZ/JvM0Q7c3RTq24JasobOQ5Rih7tFhbqCOw0DaFhEggzNwzRgonPfdW3Ntgp
v2ZWOfkYNQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,122 @@
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Artom Lifshitz <artom.lifshitz@enovance.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 eventlet
import os
import socket
import ssl
from designate import exceptions
from designate.tests.test_backend import BackendTestCase
from mock import MagicMock
# impl_nsd4slave needs to register its options before being instanciated.
# Import it and pretend to use it to avoid flake8 unused import errors.
from designate.backend import impl_nsd4slave
impl_nsd4slave
class NSD4ServerStub:
recved_command = None
response = 'ok'
keyfile = os.path.join(os.path.dirname(__file__), 'nsd_server.key')
certfile = os.path.join(os.path.dirname(__file__), 'nsd_server.pem')
def handle(self, client_sock, client_addr):
stream = client_sock.makefile()
self.recved_command = stream.readline()
stream.write(self.response)
stream.flush()
def start(self):
self.port = 1025
while True:
try:
eventlet.spawn_n(eventlet.serve,
eventlet.wrap_ssl(
eventlet.listen(('127.0.0.1', self.port)),
keyfile=self.keyfile,
certfile=self.certfile,
server_side=True),
self.handle)
break
except socket.error:
self.port = self.port + 1
def stop(self):
eventlet.StopServe()
class NSD4SlaveBackendTestCase(BackendTestCase):
__test__ = True
def setUp(self):
super(NSD4SlaveBackendTestCase, self).setUp()
self.servers = [NSD4ServerStub(), NSD4ServerStub()]
[server.start() for server in self.servers]
impl_nsd4slave.DEFAULT_PORT = self.servers[0].port
self.config(backend_driver='nsd4slave', group='service:agent')
self.config(
servers=['127.0.0.1', '127.0.0.1:%d' % self.servers[1].port],
group='backend:nsd4slave')
keyfile = os.path.join(os.path.dirname(__file__), 'nsd_control.key')
certfile = os.path.join(os.path.dirname(__file__), 'nsd_control.pem')
self.config(keyfile=keyfile, group='backend:nsd4slave')
self.config(certfile=certfile, group='backend:nsd4slave')
self.config(pattern='test-pattern', group='backend:nsd4slave')
self.nsd4 = self.get_backend_driver()
def tearDown(self):
super(NSD4SlaveBackendTestCase, self).tearDown()
[server.stop() for server in self.servers]
def test_create_domain(self):
context = self.get_context()
domain = self.get_domain_fixture()
self.nsd4.create_domain(context, domain)
command = 'NSDCT1 addzone %s test-pattern\n' % domain['name']
[self.assertEqual(server.recved_command, command)
for server in self.servers]
def test_delete_domain(self):
context = self.get_context()
domain = self.get_domain_fixture()
self.nsd4.delete_domain(context, domain)
command = 'NSDCT1 delzone %s\n' % domain['name']
[self.assertEqual(server.recved_command, command)
for server in self.servers]
def test_server_not_ok(self):
self.servers[0].response = 'goat'
context = self.get_context()
domain = self.get_domain_fixture()
self.assertRaises(exceptions.NSD4SlaveBackendError,
self.nsd4.create_domain,
context, domain)
def test_ssl_error(self):
self.nsd4._command = MagicMock(side_effet=ssl.SSLError)
context = self.get_context()
domain = self.get_domain_fixture()
self.assertRaises(exceptions.NSD4SlaveBackendError,
self.nsd4.create_domain,
context, domain)
def test_socket_error(self):
self.nsd4._command = MagicMock(side_effet=socket.error)
context = self.get_context()
domain = self.get_domain_fixture()
self.assertRaises(exceptions.NSD4SlaveBackendError,
self.nsd4.create_domain,
context, domain)

View File

@ -173,3 +173,12 @@ root_helper = sudo
#idle_timeout = 3600
#max_retries = 10
#retry_interval = 10
#-----------------------
# NSD4Slave Backend
#-----------------------
[backend:nsd4slave]
#keyfile=/etc/nsd/nsd_control.key
#certfile=/etc/nsd/nsd_control.pem
#servers=127.0.0.1,127.0.1.1:4242
#pattern=slave

View File

@ -67,6 +67,7 @@ designate.backend =
rpc = designate.backend.impl_rpc:RPCBackend
dnsmasq = designate.backend.impl_dnsmasq:DnsmasqBackend
fake = designate.backend.impl_fake:FakeBackend
nsd4slave = designate.backend.impl_nsd4slave:NSD4SlaveBackend
designate.quota =
noop = designate.quota.impl_noop:NoopQuota