Add a designate V2 API dns driver
Change-Id: Iafb36333a37146787c57eded139b4c2e071d69b0
This commit is contained in:
parent
7b3483723a
commit
62a2385790
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added support for designate v2 api with a new dns driver. To use this driver
|
||||||
|
set dns_driver = trove.dns.designate.driver.DesignateDriverV2
|
@ -144,6 +144,10 @@ common_opts = [
|
|||||||
help='Region name for DNSaaS.'),
|
help='Region name for DNSaaS.'),
|
||||||
cfg.URIOpt('dns_auth_url', default="http://0.0.0.0",
|
cfg.URIOpt('dns_auth_url', default="http://0.0.0.0",
|
||||||
help='Authentication URL for DNSaaS.'),
|
help='Authentication URL for DNSaaS.'),
|
||||||
|
cfg.StrOpt('dns_user_domain_id', default="default",
|
||||||
|
help='Keystone user domain ID used for auth'),
|
||||||
|
cfg.StrOpt('dns_project_domain_id', default="default",
|
||||||
|
help='Keystone project domain ID used for auth'),
|
||||||
cfg.StrOpt('dns_domain_name', default="",
|
cfg.StrOpt('dns_domain_name', default="",
|
||||||
help='Domain name used for adding DNS entries.'),
|
help='Domain name used for adding DNS entries.'),
|
||||||
cfg.StrOpt('dns_username', default="", secret=True,
|
cfg.StrOpt('dns_username', default="", secret=True,
|
||||||
|
@ -20,8 +20,10 @@ Dns Driver that uses Designate DNSaaS.
|
|||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from designateclient.v1 import Client
|
from designateclient import client
|
||||||
from designateclient.v1.records import Record
|
from designateclient.v1.records import Record
|
||||||
|
from keystoneauth1 import loading
|
||||||
|
from keystoneauth1 import session
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import encodeutils
|
from oslo_utils import encodeutils
|
||||||
import six
|
import six
|
||||||
@ -44,14 +46,16 @@ DNS_PASSKEY = CONF.dns_passkey
|
|||||||
DNS_TTL = CONF.dns_ttl
|
DNS_TTL = CONF.dns_ttl
|
||||||
DNS_DOMAIN_ID = CONF.dns_domain_id
|
DNS_DOMAIN_ID = CONF.dns_domain_id
|
||||||
DNS_DOMAIN_NAME = CONF.dns_domain_name
|
DNS_DOMAIN_NAME = CONF.dns_domain_name
|
||||||
|
DNS_USER_DOMAIN_ID = CONF.dns_user_domain_id
|
||||||
|
DNS_PROJECT_DOMAIN_ID = CONF.dns_project_domain_id
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DesignateObjectConverter(object):
|
class DesignateObjectConverter(object):
|
||||||
|
|
||||||
def domain_to_zone(self, domain):
|
@staticmethod
|
||||||
|
def domain_to_zone(domain):
|
||||||
return DesignateDnsZone(id=domain.id, name=domain.name)
|
return DesignateDnsZone(id=domain.id, name=domain.name)
|
||||||
|
|
||||||
def record_to_entry(self, record, dns_zone):
|
def record_to_entry(self, record, dns_zone):
|
||||||
@ -60,22 +64,23 @@ class DesignateObjectConverter(object):
|
|||||||
priority=record.priority, dns_zone=dns_zone)
|
priority=record.priority, dns_zone=dns_zone)
|
||||||
|
|
||||||
|
|
||||||
def create_designate_client():
|
def create_designate_client(api_version='2'):
|
||||||
"""Creates a Designate DNSaaS client."""
|
"""Creates a Designate DNSaaS client."""
|
||||||
client = Client(auth_url=DNS_AUTH_URL,
|
loader = loading.get_plugin_loader('password')
|
||||||
username=DNS_USERNAME,
|
auth = loader.load_from_options(auth_url=DNS_AUTH_URL,
|
||||||
password=DNS_PASSKEY,
|
username=DNS_USERNAME,
|
||||||
tenant_id=DNS_TENANT_ID,
|
password=DNS_PASSKEY,
|
||||||
endpoint=DNS_ENDPOINT_URL,
|
project_id=DNS_TENANT_ID,
|
||||||
service_type=DNS_SERVICE_TYPE,
|
user_domain_id=DNS_USER_DOMAIN_ID,
|
||||||
region_name=DNS_REGION)
|
project_domain_id=DNS_PROJECT_DOMAIN_ID)
|
||||||
return client
|
sesh = session.Session(auth=auth)
|
||||||
|
return client.Client(api_version, session=sesh)
|
||||||
|
|
||||||
|
|
||||||
class DesignateDriver(driver.DnsDriver):
|
class DesignateDriver(driver.DnsDriver):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.dns_client = create_designate_client()
|
self.dns_client = create_designate_client(api_version='1')
|
||||||
self.converter = DesignateObjectConverter()
|
self.converter = DesignateObjectConverter()
|
||||||
self.default_dns_zone = DesignateDnsZone(id=DNS_DOMAIN_ID,
|
self.default_dns_zone = DesignateDnsZone(id=DNS_DOMAIN_ID,
|
||||||
name=DNS_DOMAIN_NAME)
|
name=DNS_DOMAIN_NAME)
|
||||||
@ -140,6 +145,46 @@ class DesignateDriver(driver.DnsDriver):
|
|||||||
return self.dns_client.records.list(dns_zone.id)
|
return self.dns_client.records.list(dns_zone.id)
|
||||||
|
|
||||||
|
|
||||||
|
class DesignateDriverV2(driver.DnsDriver):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.dns_client = create_designate_client()
|
||||||
|
self.default_dns_zone = DesignateDnsZone(id=DNS_DOMAIN_ID,
|
||||||
|
name=DNS_DOMAIN_NAME)
|
||||||
|
|
||||||
|
def create_entry(self, entry, content):
|
||||||
|
"""Creates the entry in the driver at the given dns zone."""
|
||||||
|
dns_zone = entry.dns_zone or self.default_dns_zone
|
||||||
|
if not dns_zone.id:
|
||||||
|
raise TypeError(_("The entry's dns_zone must have an ID "
|
||||||
|
"specified."))
|
||||||
|
name = entry.name
|
||||||
|
LOG.debug("Creating DNS entry %s.", name)
|
||||||
|
client = self.dns_client
|
||||||
|
# Record name has to end with a '.' by dns standard
|
||||||
|
client.recordsets.create(DNS_DOMAIN_ID, entry.name + '.', entry.type,
|
||||||
|
records=[content])
|
||||||
|
|
||||||
|
def delete_entry(self, name, type, dns_zone=None):
|
||||||
|
"""Deletes an entry with the given name and type from a dns zone."""
|
||||||
|
dns_zone = dns_zone or self.default_dns_zone
|
||||||
|
records = self._get_records(dns_zone)
|
||||||
|
matching_record = [rec for rec in records
|
||||||
|
if rec['name'] == name + '.'
|
||||||
|
and rec['type'] == type]
|
||||||
|
if not matching_record:
|
||||||
|
raise exception.DnsRecordNotFound(name)
|
||||||
|
LOG.debug("Deleting DNS entry %s.", name)
|
||||||
|
self.dns_client.recordsets.delete(dns_zone.id,
|
||||||
|
matching_record[0]['id'])
|
||||||
|
|
||||||
|
def _get_records(self, dns_zone):
|
||||||
|
dns_zone = dns_zone or self.default_dns_zone
|
||||||
|
if not dns_zone:
|
||||||
|
raise TypeError(_('DNS domain is must be specified'))
|
||||||
|
return self.dns_client.recordsets.list(dns_zone.id)
|
||||||
|
|
||||||
|
|
||||||
class DesignateInstanceEntryFactory(driver.DnsInstanceEntryFactory):
|
class DesignateInstanceEntryFactory(driver.DnsInstanceEntryFactory):
|
||||||
"""Defines how instance DNS entries are created for instances."""
|
"""Defines how instance DNS entries are created for instances."""
|
||||||
|
|
||||||
|
@ -20,7 +20,9 @@ from mock import MagicMock
|
|||||||
from mock import patch
|
from mock import patch
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from trove.common import exception
|
||||||
from trove.dns.designate import driver
|
from trove.dns.designate import driver
|
||||||
|
from trove.dns import driver as base_driver
|
||||||
from trove.tests.unittests import trove_testtools
|
from trove.tests.unittests import trove_testtools
|
||||||
|
|
||||||
|
|
||||||
@ -173,6 +175,56 @@ class DesignateDriverTest(trove_testtools.TestCase):
|
|||||||
self.assertEqual(expected.id, actual.id)
|
self.assertEqual(expected.id, actual.id)
|
||||||
|
|
||||||
|
|
||||||
|
class DesignateDriverV2Test(trove_testtools.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(DesignateDriverV2Test, self).setUp()
|
||||||
|
self.records = [dict(name='record1.', type='A', data='10.0.0.1',
|
||||||
|
ttl=3600, priority=1,
|
||||||
|
id='11111111-1111-1111-1111-111111111111'),
|
||||||
|
dict(name='record2.', type='CNAME', data='10.0.0.2',
|
||||||
|
ttl=1800, priority=2,
|
||||||
|
id='22222222-2222-2222-2222-222222222222'),
|
||||||
|
dict(name='record3.', type='A', data='10.0.0.3',
|
||||||
|
ttl=3600, priority=1,
|
||||||
|
id='3333333-3333-3333-3333-333333333333')]
|
||||||
|
self.mock_client = MagicMock()
|
||||||
|
self.create_des_client_patch = patch.object(
|
||||||
|
driver, 'create_designate_client', MagicMock(
|
||||||
|
return_value=self.mock_client))
|
||||||
|
self.create_des_client_mock = self.create_des_client_patch.start()
|
||||||
|
self.addCleanup(self.create_des_client_patch.stop)
|
||||||
|
|
||||||
|
def test_create_entry(self):
|
||||||
|
dns_driver = driver.DesignateDriverV2()
|
||||||
|
zone = driver.DesignateDnsZone(
|
||||||
|
id='22222222-2222-2222-2222-222222222222', name='www.trove.com')
|
||||||
|
entry = base_driver.DnsEntry(name='www.example.com', content='None',
|
||||||
|
type='A', ttl=3600, priority=None,
|
||||||
|
dns_zone=zone)
|
||||||
|
|
||||||
|
dns_driver.create_entry(entry, '1.2.3.4')
|
||||||
|
self.mock_client.recordsets.create.assert_called_once_with(
|
||||||
|
driver.DNS_DOMAIN_ID, entry.name + '.', entry.type,
|
||||||
|
records=['1.2.3.4'])
|
||||||
|
|
||||||
|
def test_delete_entry(self):
|
||||||
|
with patch.object(driver.DesignateDriverV2, '_get_records',
|
||||||
|
MagicMock(return_value=self.records)):
|
||||||
|
dns_driver = driver.DesignateDriverV2()
|
||||||
|
dns_driver.delete_entry('record1', 'A')
|
||||||
|
self.mock_client.recordsets.delete(driver.DNS_DOMAIN_ID)
|
||||||
|
|
||||||
|
def test_delete_no_entry(self):
|
||||||
|
with patch.object(driver.DesignateDriverV2, '_get_records',
|
||||||
|
MagicMock(return_value=self.records)):
|
||||||
|
dns_driver = driver.DesignateDriverV2()
|
||||||
|
self.assertRaises(exception.DnsRecordNotFound,
|
||||||
|
dns_driver.delete_entry,
|
||||||
|
'nothere', 'A')
|
||||||
|
self.mock_client.recordsets.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
class DesignateInstanceEntryFactoryTest(trove_testtools.TestCase):
|
class DesignateInstanceEntryFactoryTest(trove_testtools.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user