Merge "nsd4slave backend"
This commit is contained in:
commit
110a27c436
124
designate/backend/impl_nsd4slave.py
Normal file
124
designate/backend/impl_nsd4slave.py
Normal 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
|
@ -34,6 +34,10 @@ class Backend(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NSD4SlaveBackendError(Backend):
|
||||
pass
|
||||
|
||||
|
||||
class NotImplemented(Base, NotImplementedError):
|
||||
pass
|
||||
|
||||
|
21
designate/tests/test_backend/nsd_control.key
Normal file
21
designate/tests/test_backend/nsd_control.key
Normal 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-----
|
14
designate/tests/test_backend/nsd_control.pem
Normal file
14
designate/tests/test_backend/nsd_control.pem
Normal 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-----
|
21
designate/tests/test_backend/nsd_server.key
Normal file
21
designate/tests/test_backend/nsd_server.key
Normal 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-----
|
14
designate/tests/test_backend/nsd_server.pem
Normal file
14
designate/tests/test_backend/nsd_server.pem
Normal 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-----
|
122
designate/tests/test_backend/test_nsd4slave.py
Normal file
122
designate/tests/test_backend/test_nsd4slave.py
Normal 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)
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user