Implement create/delete zone for Akamai v2 API

- Ignore duplicate Zone error
- Handle error when contractId or gid is missed
- Ignore port for masters servers, because Akamai uses only 53 port and
does not allow to specify any port in list of masters servers.
- Added timeout and retries for soft Zone Delete
- Added handling errors on the delete zone action
- Added Log info message with RequestId on soft zone delete
- Added processing for TsigKey during creation zone
- Added devsatck_plugin for akamai_v2 backend

Depends-On: https://review.opendev.org/#/c/692819/4

Change-Id: Ib221f4cf0371e70fc6900582d826ffc1bdfc12b9
This commit is contained in:
Sergey Kraynev 2019-10-03 23:38:10 +04:00 committed by Erik Olof Gunnar Andersson
parent f355aae939
commit 318b8d0319
7 changed files with 897 additions and 0 deletions

View File

@ -0,0 +1,199 @@
# Copyright 2019 Cloudification GmbH
#
# Author: Sergey Kraynev <contact@cloudification.io>
#
# 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 time
import requests
from akamai import edgegrid
from oslo_log import log as logging
import six.moves.urllib.parse as urlparse
from designate import exceptions
from designate.backend import base
LOG = logging.getLogger(__name__)
class AkamaiClient(object):
def __init__(self, client_token=None, client_secret=None,
access_token=None, host=None):
session = requests.Session()
self.baseurl = 'https://%s' % host
self.client_token = client_token
self.client_secret = client_secret
self.access_token = access_token
session.auth = edgegrid.EdgeGridAuth(
client_token=self.client_token,
client_secret=self.client_secret,
access_token=self.access_token
)
self.http = session
def gen_url(self, url_path):
return urlparse.urljoin(self.baseurl, url_path)
def post(self, payloads):
url_path = payloads.pop('url')
return self.http.post(url=self.gen_url(url_path), **payloads)
def get(self, url_path):
return self.http.get(url=self.gen_url(url_path))
def build_masters_field(self, masters):
# Akamai v2 supports only ip and hostnames. Ports could not be
# specified explicitly. 53 will be used by default
return [master.host for master in masters]
def gen_tsig_payload(self, target):
return {
'name': target.options.get('tsig_key_name'),
'algorithm': target.options.get('tsig_key_algorithm'),
'secret': target.options.get('tsig_key_secret'),
}
def gen_create_payload(self, zone, masters, contract_id, gid, tenant_id,
target):
if contract_id is None:
raise exceptions.Backend(
'contractId is required for zone creation')
masters = self.build_masters_field(masters)
body = {
'zone': zone['name'],
'type': 'secondary',
'comment': 'Created by Designate for Tenant %s' % tenant_id,
'masters': masters,
}
# Add tsigKey if it exists
if target.options.get('tsig_key_name'):
# It's not mentioned in doc, but json schema supports specification
# TsigKey in the same zone creation body
body.update({'tsigKey': self.gen_tsig_payload(target)})
params = {
'contractId': contract_id,
'gid': gid,
}
return {
'url': 'config-dns/v2/zones',
'params': params,
'json': body,
}
def create_zone(self, payload):
result = self.post(payload)
# NOTE: ignore error about duplicate SZ in AKAMAI
if result.status_code == 409 and result.reason == 'Conflict':
LOG.info("Can't create zone %s because it already exists",
payload['json']['zone'])
elif not result.ok:
json_res = result.json()
raise exceptions.Backend(
'Zone creation failed due to: %s' % json_res['detail'])
@staticmethod
def gen_delete_payload(zone_name, force):
return {
'url': '/config-dns/v2/zones/delete-requests',
'params': {'force': force},
'json': {'zones': [zone_name]},
}
def delete_zone(self, zone_name):
# - try to delete with force=True
# - if we get Forbidden error - try to delete it with Checks logic
result = self.post(
self.gen_delete_payload(zone_name, force=True))
if result.status_code == 403 and result.reason == 'Forbidden':
result = self.post(
self.gen_delete_payload(zone_name, force=False))
if result.ok:
request_id = result.json().get('requestId')
LOG.info('Run soft delete for zone (%s) and requestId (%s)',
zone_name, request_id)
if request_id is None:
reason = 'requestId missed in response'
raise exceptions.Backend(
'Zone deletion failed due to: %s' % reason)
self.validate_deletion_is_complete(request_id)
if not result.ok and result.status_code != 404:
reason = result.json().get('detail') or result.json()
raise exceptions.Backend(
'Zone deletion failed due to: %s' % reason)
def validate_deletion_is_complete(self, request_id):
check_url = '/config-dns/v2/zones/delete-requests/%s' % request_id
deleted = False
attempt = 0
while not deleted and attempt < 10:
result = self.get(check_url)
deleted = result.json()['isComplete']
attempt += 1
time.sleep(1.0)
if not deleted:
raise exceptions.Backend(
'Zone was not deleted after %s attempts' % attempt)
class AkamaiBackend(base.Backend):
__plugin_name__ = 'akamai_v2'
__backend_status__ = 'untested'
def __init__(self, target):
super(AkamaiBackend, self).__init__(target)
self._host = self.options.get('host', '127.0.0.1')
self._port = int(self.options.get('port', 53))
self.client = self.init_client()
def init_client(self):
baseurl = self.options.get('akamai_host', '127.0.0.1')
client_token = self.options.get('akamai_client_token', 'admin')
client_secret = self.options.get('akamai_client_secret', 'admin')
access_token = self.options.get('akamai_access_token', 'admin')
return AkamaiClient(client_token, client_secret, access_token, baseurl)
def create_zone(self, context, zone):
"""Create a DNS zone"""
LOG.debug('Create Zone')
contract_id = self.options.get('akamai_contract_id')
gid = self.options.get('akamai_gid')
project_id = context.project_id or zone.tenant_id
# Take list of masters from pools.yaml
payload = self.client.gen_create_payload(
zone, self.masters, contract_id, gid, project_id, self.target)
self.client.create_zone(payload)
self.mdns_api.notify_zone_changed(
context, zone, self._host, self._port, self.timeout,
self.retry_interval, self.max_retries, self.delay)
def delete_zone(self, context, zone):
"""Delete a DNS zone"""
LOG.debug('Delete Zone')
self.client.delete_zone(zone['name'])

View File

@ -0,0 +1,494 @@
# Copyright 2019 Cloudification GmbH
#
# Author: Sergey Kraynev <contact@cloudification.io>
#
# 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 json
import mock
import requests
import designate.tests
from designate import exceptions
from designate import objects
from designate.backend import impl_akamai_v2 as akamai
from designate.tests import fixtures
class AkamaiBackendTestCase(designate.tests.TestCase):
def setUp(self):
super(AkamaiBackendTestCase, self).setUp()
self.zone = objects.Zone(
id='cca7908b-dad4-4c50-adba-fb67d4c556e8',
name='example.com.',
email='example@example.com'
)
self.target = {
'id': '4588652b-50e7-46b9-b688-a9bad40a873e',
'type': 'akamai_v2',
'masters': [
{'host': '192.168.1.1', 'port': 53},
{'host': '192.168.1.2', 'port': 35}
],
'options': [
{'key': 'host', 'value': '192.168.2.3'},
{'key': 'port', 'value': '53'},
{'key': 'akamai_client_secret', 'value': 'client_secret'},
{'key': 'akamai_host', 'value': 'host_value'},
{'key': 'akamai_access_token', 'value': 'access_token'},
{'key': 'akamai_client_token', 'value': 'client_token'},
{'key': 'akamai_contract_id', 'value': 'G-XYW'},
{'key': 'akamai_gid', 'value': '777'}
],
}
def gen_response(self, status_code, reason, json_data=None):
response = requests.models.Response()
response.status_code = status_code
response.reason = reason
response._content = json.dumps(json_data or {}).encode('utf-8')
return response
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_create_zone_missed_contract_id(self, mock_post, mock_auth):
self.target['options'].remove(
{'key': 'akamai_contract_id', 'value': 'G-XYW'})
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
with fixtures.random_seed(0):
self.assertRaisesRegex(
exceptions.Backend,
'contractId is required for zone creation',
backend.create_zone, self.admin_context, self.zone)
mock_post.assert_not_called()
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_create_zone(self, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
with fixtures.random_seed(0):
backend.create_zone(self.admin_context, self.zone)
project_id = self.admin_context.project_id or self.zone.tenant_id
mock_post.assert_called_once_with(
json={
'comment': 'Created by Designate for Tenant %s' % project_id,
'masters': ['192.168.1.1', '192.168.1.2'],
'type': 'secondary', 'zone': u'example.com.'
},
params={
'gid': '777',
'contractId': 'G-XYW'
},
url='https://host_value/config-dns/v2/zones'
)
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_create_zone_duplicate_zone(self, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
mock_post.return_value = self.gen_response(409, 'Conflict')
with fixtures.random_seed(0):
backend.create_zone(self.admin_context, self.zone)
project_id = self.admin_context.project_id or self.zone.tenant_id
mock_post.assert_called_once_with(
json={
'comment': 'Created by Designate for Tenant %s' % project_id,
'masters': ['192.168.1.1', '192.168.1.2'],
'type': 'secondary', 'zone': u'example.com.'
},
params={
'gid': '777',
'contractId': 'G-XYW'
},
url='https://host_value/config-dns/v2/zones'
)
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_create_zone_with_tsig_key(self, mock_post, mock_auth):
self.target['options'].extend([
{'key': 'tsig_key_name', 'value': 'test_key'},
{'key': 'tsig_key_algorithm', 'value': 'hmac-sha512'},
{'key': 'tsig_key_secret', 'value': 'aaaabbbbccc'}
])
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
with fixtures.random_seed(0):
backend.create_zone(self.admin_context, self.zone)
project_id = self.admin_context.project_id or self.zone.tenant_id
mock_post.assert_called_once_with(
json={
'comment': 'Created by Designate for Tenant %s' % project_id,
'masters': ['192.168.1.1', '192.168.1.2'],
'type': 'secondary',
'zone': 'example.com.',
'tsigKey': {
'name': 'test_key',
'algorithm': 'hmac-sha512',
'secret': 'aaaabbbbccc',
}
},
params={
'gid': '777',
'contractId': 'G-XYW'
},
url='https://host_value/config-dns/v2/zones'
)
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_create_zone_raise_error(self, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
json_data = {
'title': 'Missing parameter',
'detail': 'Missed A option'
}
mock_post.return_value = self.gen_response(
400, 'Bad Request', json_data)
with fixtures.random_seed(0):
self.assertRaisesRegex(
exceptions.Backend,
'Zone creation failed due to: Missed A option',
backend.create_zone, self.admin_context, self.zone)
project_id = self.admin_context.project_id or self.zone.tenant_id
mock_post.assert_called_once_with(
json={
'comment': 'Created by Designate for Tenant %s' % project_id,
'masters': ['192.168.1.1', '192.168.1.2'],
'type': 'secondary', 'zone': 'example.com.'
},
params={
'gid': '777',
'contractId': 'G-XYW'
},
url='https://host_value/config-dns/v2/zones'
)
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_force_delete_zone(self, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
mock_post.return_value = self.gen_response(200, 'Success')
with fixtures.random_seed(0):
backend.delete_zone(self.admin_context, self.zone)
mock_post.assert_called_once_with(
json={
'zones': ['example.com.']
},
params={
'force': True
},
url='https://host_value/config-dns/v2/zones/delete-requests'
)
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_force_delete_zone_raise_error(self, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
mock_post.return_value = self.gen_response(
403, 'Bad Request', {'detail': 'Unexpected error'})
with fixtures.random_seed(0):
self.assertRaisesRegex(
exceptions.Backend,
'Zone deletion failed due to: Unexpected error',
backend.delete_zone, self.admin_context, self.zone)
mock_post.assert_called_once_with(
json={
'zones': ['example.com.']
},
params={
'force': True
},
url='https://host_value/config-dns/v2/zones/delete-requests'
)
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_force_delete_zone_raise_error_404(self, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
mock_post.return_value = self.gen_response(
404, 'Bad Request', {'detail': 'Unexpected error'})
with fixtures.random_seed(0):
backend.delete_zone(self.admin_context, self.zone)
mock_post.assert_called_once_with(
json={
'zones': ['example.com.']
},
params={
'force': True
},
url='https://host_value/config-dns/v2/zones/delete-requests'
)
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
@mock.patch.object(akamai.requests.Session, 'get')
def test_soft_delete_zone(self, mock_get, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
mock_post.side_effect = [
# emulate, when Force=True is forbidden
self.gen_response(403, 'Forbidden'),
# emulate request, when Force=False
self.gen_response(200, 'Success', {'requestId': 'nice_id'}),
]
# emulate max 9 failed attempts and 1 success
mock_get.side_effect = 9 * [
self.gen_response(200, 'Success', {'isComplete': False})
] + [
self.gen_response(200, 'Success', {'isComplete': True})
]
with fixtures.random_seed(0), \
mock.patch.object(akamai.time, 'sleep') as mock_sleep:
mock_sleep.return_value = None
backend.delete_zone(self.admin_context, self.zone)
self.assertEqual(10, mock_sleep.call_count)
url = 'https://host_value/config-dns/v2/zones/delete-requests/nice_id'
mock_get.assert_has_calls(9 * [mock.call(url=url)])
mock_post.assert_has_calls([
mock.call(
json={'zones': ['example.com.']},
params={'force': True},
url='https://host_value/config-dns/v2/zones/delete-requests'
),
mock.call(
json={'zones': ['example.com.']},
params={'force': False},
url='https://host_value/config-dns/v2/zones/delete-requests'
)
])
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
@mock.patch.object(akamai.requests.Session, 'get')
def test_soft_delete_zone_failed_after_10_attempts(
self, mock_get, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
mock_post.side_effect = [
# emulate, when Force=True is forbidden
self.gen_response(403, 'Forbidden'),
# emulate request, when Force=False
self.gen_response(200, 'Success', {'requestId': 'nice_id'}),
]
# emulate max 10 failed attempts
mock_get.side_effect = 10 * [
self.gen_response(200, 'Success', {'isComplete': False})
]
with fixtures.random_seed(0), \
mock.patch.object(akamai.time, 'sleep') as mock_sleep:
mock_sleep.return_value = None
self.assertRaisesRegex(
exceptions.Backend,
'Zone was not deleted after 10 attempts',
backend.delete_zone, self.admin_context, self.zone)
self.assertEqual(10, mock_sleep.call_count)
url = 'https://host_value/config-dns/v2/zones/delete-requests/nice_id'
mock_get.assert_has_calls(10 * [mock.call(url=url)])
mock_post.assert_has_calls([
mock.call(
json={'zones': ['example.com.']},
params={'force': True},
url='https://host_value/config-dns/v2/zones/delete-requests'
),
mock.call(
json={'zones': ['example.com.']},
params={'force': False},
url='https://host_value/config-dns/v2/zones/delete-requests'
)
])
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_soft_delete_zone_raise_error(self, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
mock_post.side_effect = [
# emulate, when Force=True is forbidden
self.gen_response(403, 'Forbidden'),
# emulate request, when Force=False
self.gen_response(409, 'Conflict', {'detail': 'Intenal Error'})
]
with fixtures.random_seed(0):
self.assertRaisesRegex(
exceptions.Backend,
'Zone deletion failed due to: Intenal Error',
backend.delete_zone, self.admin_context, self.zone)
mock_post.assert_has_calls([
mock.call(
json={'zones': [u'example.com.']},
params={'force': True},
url='https://host_value/config-dns/v2/zones/delete-requests'
),
mock.call(
json={'zones': [u'example.com.']},
params={'force': False},
url='https://host_value/config-dns/v2/zones/delete-requests'
)
])
@mock.patch.object(akamai, 'edgegrid')
@mock.patch.object(akamai.requests.Session, 'post')
def test_soft_delete_zone_missed_request_id(self, mock_post, mock_auth):
backend = akamai.AkamaiBackend(
objects.PoolTarget.from_dict(self.target)
)
mock_auth.EdgeGridAuth.assert_called_once_with(
access_token='access_token',
client_secret='client_secret',
client_token='client_token'
)
mock_post.side_effect = [
# emulate, when Force=True is forbidden
self.gen_response(403, 'Forbidden'),
# emulate request, when Force=False
self.gen_response(200, 'Success')
]
with fixtures.random_seed(0):
self.assertRaisesRegex(
exceptions.Backend,
'Zone deletion failed due to: requestId missed in response',
backend.delete_zone, self.admin_context, self.zone)
mock_post.assert_has_calls([
mock.call(
json={'zones': [u'example.com.']},
params={'force': True},
url='https://host_value/config-dns/v2/zones/delete-requests'
),
mock.call(
json={'zones': [u'example.com.']},
params={'force': False},
url='https://host_value/config-dns/v2/zones/delete-requests'
)
])

View File

@ -0,0 +1,161 @@
# Configure the Akamai v2 backend
# Requirements:
# An active Akamai account / contract will be requied to use this DevStack
# plugin.
# Enable with:
# DESIGNATE_BACKEND_DRIVER=akamai_v2
# Dependencies:
# ``functions`` file
# ``designate`` configuration
# install_designate_backend - install any external requirements
# configure_designate_backend - make configuration changes, including those to other services
# init_designate_backend - initialize databases, etc.
# start_designate_backend - start any external services
# stop_designate_backend - stop any external services
# cleanup_designate_backend - remove transient data and cache
# Save trace setting
DP_AKAMAI_XTRACE=$(set +o | grep xtrace)
set +o xtrace
# Defaults
# --------
# DESIGNATE_HOST is IP address of the one of AKAMAI_NAMESERVERS
DESIGNATE_HOST=${DESIGNATE_HOST:-"193.108.91.197"}
DESIGNATE_AKAMAI_CLIENT_SECRET=${DESIGNATE_AKAMAI_CLIENT_SECRET:-"client_secret_string"}
DESIGNATE_AKAMAI_HOST=${DESIGNATE_AKAMAI_HOST:-"akamai_host_string"}
DESIGNATE_AKAMAI_ACCESS_TOKEN=${DESIGNATE_AKAMAI_ACCESS_TOKEN:-"access_token_string"}
DESIGNATE_AKAMAI_CLIENT_TOKEN=${DESIGNATE_AKAMAI_CLIENT_TOKEN:-"client_token_string"}
DESIGNATE_AKAMAI_CONTRACT_ID=${DESIGNATE_AKAMAI_CONTRACT_ID:-"contract_id"}
DESIGNATE_AKAMAI_GID=${DESIGNATE_AKAMAI_GID:-"group_id"}
DESIGNATE_AKAMAI_MASTERS=${DESIGNATE_AKAMAI_MASTERS:-"$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_MDNS"}
DESIGNATE_AKAMAI_NAMESERVERS=${DESIGNATE_AKAMAI_NAMESERVERS:-""}
DESIGNATE_AKAMAI_ALSO_NOTIFIES=${DESIGNATE_AKAMAI_ALSO_NOTIFIES:-"23.14.128.185,23.207.197.166,23.205.121.134,104.122.95.88,72.247.124.98"}
# Sanity Checks
# -------------
if [ -z "$DESIGNATE_AKAMAI_NAMESERVERS" ]; then
die $LINENO "You must configure DESIGNATE_AKAMAI_NAMESERVERS"
fi
if [ "$DESIGNATE_SERVICE_PORT_MDNS" != "53" ]; then
die $LINENO "Akamai requires DESIGNATE_SERVICE_PORT_MDNS is set to '53'"
fi
# Entry Points
# ------------
# install_designate_backend - install any external requirements
function install_designate_backend {
:
}
# configure_designate_backend - make configuration changes, including those to other services
function configure_designate_backend {
# Generate Designate pool.yaml file
sudo tee $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
---
- name: default
description: DevStack Akamai Pool
attributes: {}
targets:
- type: akamai
description: Akamai API
options:
host: $DESIGNATE_HOST
port: 53
akamai_client_secret: $DESIGNATE_AKAMAI_CLIENT_SECRET
akamai_host: $DESIGNATE_AKAMAI_HOST
akamai_access_token: $DESIGNATE_AKAMAI_ACCESS_TOKEN
akamai_client_token: $DESIGNATE_AKAMAI_CLIENT_TOKEN
akamai_contract_id: $DESIGNATE_AKAMAI_CONTRACT_ID
akamai_gid: $DESIGNATE_AKAMAI_GID
# NOTE: TSIG key has to be set manully if it's necessary
#tsig_key_name: key_test
#tsig_key_algorithm: hmac-sha512
#tsig_key_secret: test_ley_secret
masters:
EOF
# Create a Pool Master for each of the Akamai Masters
IFS=',' read -a masters <<< "$DESIGNATE_AKAMAI_MASTERS"
for master in "${masters[@]}"; do
sudo tee -a $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
- host: $master
port: 53
EOF
done
# Create a Pool NS Record for each of the Akamai Nameservers
IFS=',' read -a nameservers <<< "$DESIGNATE_AKAMAI_NAMESERVERS"
sudo tee -a $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
ns_records:
EOF
for nameserver in "${nameservers[@]}"; do
sudo tee -a $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
- hostname: $nameserver
priority: 1
EOF
done
# Create a Pool Nameserver for each of the Akamai Nameservers
sudo tee -a $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
nameservers:
EOF
for nameserver in "${nameservers[@]}"; do
sudo tee -a $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
- host: `dig +short A $nameserver | head -n 1`
port: 53
EOF
done
# Create a Pool Also Notifies for each of the Akamai Also Notifies
IFS=',' read -a also_notifies <<< "$DESIGNATE_AKAMAI_ALSO_NOTIFIES"
sudo tee -a $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
also_notifies:
EOF
for also_notify in "${also_notifies[@]}"; do
sudo tee -a $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
- host: $also_notify
port: 53
EOF
done
}
# init_designate_backend - initialize databases, etc.
function init_designate_backend {
:
}
# start_designate_backend - start any external services
function start_designate_backend {
:
}
# stop_designate_backend - stop any external services
function stop_designate_backend {
:
}
# cleanup_designate_backend - remove transient data and cache
function cleanup_designate_backend {
:
}
# Restore xtrace
$DP_AKAMAI_XTRACE

View File

@ -0,0 +1,40 @@
- name: default-akamai-v2
# The name is immutable. There will be no option to change the name after
# creation and the only way will to change it will be to delete it
# (and all zones associated with it) and recreate it.
description: Akamai v2
attributes: {}
# List out the NS records for zones hosted within this pool
ns_records:
- hostname: ns1-1.example.org.
priority: 1
# List out the nameservers for this pool. These are the actual Akamai servers.
# We use these to verify changes have propagated to all nameservers.
nameservers:
- host: 192.0.2.2
port: 53
# List out the targets for this pool. For Akamai, most often, there will be
# one entry for each Akamai server.
targets:
- type: akamai_v2
description: Akamai v2 server
# List out the designate-mdns servers from which Akamai servers should
# request zone transfers (AXFRs) from.
masters:
- host: 192.0.2.1
port: 5354
options:
host: 192.0.2.2
port: 53
akamai_host: 192.0.2.2
akamai_client_token: client_token_string
akamai_access_token: access_token_string
akamai_client_secret: client_secret_string
akamai_contract_id: contract_id
akamai_gid: group_id

View File

@ -27,6 +27,7 @@ doc8==0.6.0
docutils==0.14
dogpile.cache==0.6.5
dulwich==0.19.0
edgegrid-python==1.1.1
enum-compat==0.0.2
eventlet==0.18.2
extras==1.0.0

View File

@ -49,3 +49,4 @@ debtcollector>=1.2.0 # Apache-2.0
os-win>=3.0.0 # Apache-2.0
monasca-statsd>=1.1.0 # Apache-2.0
futurist>=1.2.0 # Apache-2.0
edgegrid-python>=1.1.1 # Apache-2.0

View File

@ -75,6 +75,7 @@ designate.backend =
pdns4 = designate.backend.impl_pdns4:PDNS4Backend
dynect = designate.backend.impl_dynect:DynECTBackend
akamai = designate.backend.impl_akamai:AkamaiBackend
akamai_v2 = designate.backend.impl_akamai_v2:AkamaiBackend
nsd4 = designate.backend.impl_nsd4:NSD4Backend
infoblox = designate.backend.impl_infoblox:InfobloxBackend
fake = designate.backend.impl_fake:FakeBackend