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.'),
|
||||
cfg.URIOpt('dns_auth_url', default="http://0.0.0.0",
|
||||
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="",
|
||||
help='Domain name used for adding DNS entries.'),
|
||||
cfg.StrOpt('dns_username', default="", secret=True,
|
||||
|
@ -20,8 +20,10 @@ Dns Driver that uses Designate DNSaaS.
|
||||
import base64
|
||||
import hashlib
|
||||
|
||||
from designateclient.v1 import Client
|
||||
from designateclient import client
|
||||
from designateclient.v1.records import Record
|
||||
from keystoneauth1 import loading
|
||||
from keystoneauth1 import session
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import encodeutils
|
||||
import six
|
||||
@ -44,14 +46,16 @@ DNS_PASSKEY = CONF.dns_passkey
|
||||
DNS_TTL = CONF.dns_ttl
|
||||
DNS_DOMAIN_ID = CONF.dns_domain_id
|
||||
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__)
|
||||
|
||||
|
||||
class DesignateObjectConverter(object):
|
||||
|
||||
def domain_to_zone(self, domain):
|
||||
@staticmethod
|
||||
def domain_to_zone(domain):
|
||||
return DesignateDnsZone(id=domain.id, name=domain.name)
|
||||
|
||||
def record_to_entry(self, record, dns_zone):
|
||||
@ -60,22 +64,23 @@ class DesignateObjectConverter(object):
|
||||
priority=record.priority, dns_zone=dns_zone)
|
||||
|
||||
|
||||
def create_designate_client():
|
||||
def create_designate_client(api_version='2'):
|
||||
"""Creates a Designate DNSaaS client."""
|
||||
client = Client(auth_url=DNS_AUTH_URL,
|
||||
loader = loading.get_plugin_loader('password')
|
||||
auth = loader.load_from_options(auth_url=DNS_AUTH_URL,
|
||||
username=DNS_USERNAME,
|
||||
password=DNS_PASSKEY,
|
||||
tenant_id=DNS_TENANT_ID,
|
||||
endpoint=DNS_ENDPOINT_URL,
|
||||
service_type=DNS_SERVICE_TYPE,
|
||||
region_name=DNS_REGION)
|
||||
return client
|
||||
project_id=DNS_TENANT_ID,
|
||||
user_domain_id=DNS_USER_DOMAIN_ID,
|
||||
project_domain_id=DNS_PROJECT_DOMAIN_ID)
|
||||
sesh = session.Session(auth=auth)
|
||||
return client.Client(api_version, session=sesh)
|
||||
|
||||
|
||||
class DesignateDriver(driver.DnsDriver):
|
||||
|
||||
def __init__(self):
|
||||
self.dns_client = create_designate_client()
|
||||
self.dns_client = create_designate_client(api_version='1')
|
||||
self.converter = DesignateObjectConverter()
|
||||
self.default_dns_zone = DesignateDnsZone(id=DNS_DOMAIN_ID,
|
||||
name=DNS_DOMAIN_NAME)
|
||||
@ -140,6 +145,46 @@ class DesignateDriver(driver.DnsDriver):
|
||||
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):
|
||||
"""Defines how instance DNS entries are created for instances."""
|
||||
|
||||
|
@ -20,7 +20,9 @@ from mock import MagicMock
|
||||
from mock import patch
|
||||
import six
|
||||
|
||||
from trove.common import exception
|
||||
from trove.dns.designate import driver
|
||||
from trove.dns import driver as base_driver
|
||||
from trove.tests.unittests import trove_testtools
|
||||
|
||||
|
||||
@ -173,6 +175,56 @@ class DesignateDriverTest(trove_testtools.TestCase):
|
||||
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):
|
||||
|
||||
def setUp(self):
|
||||
|
Loading…
Reference in New Issue
Block a user