6951fcf058
Change-Id: I94321c3538fcfca5503c1f716988ec5f574342a0
259 lines
8.5 KiB
Python
259 lines
8.5 KiB
Python
# Copyright 2012-2015 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# Author: Kiall Mac Innes <kiall@hp.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 os
|
|
|
|
from oslo_log import log as logging
|
|
from oslo_config import cfg
|
|
from suds.client import Client as SudsClient
|
|
from suds.transport.https import HttpAuthenticated
|
|
|
|
from designate import exceptions
|
|
from designate import utils
|
|
from designate.backend import base
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
CONF = cfg.CONF
|
|
|
|
WSDL = os.path.join(os.path.dirname(__file__),
|
|
'..',
|
|
'resources',
|
|
'wsdl',
|
|
'EnhancedDNS.xml')
|
|
|
|
|
|
class EnhancedDNSException(exceptions.Backend):
|
|
pass
|
|
|
|
|
|
class DelegationExists(exceptions.BadRequest, EnhancedDNSException):
|
|
"""
|
|
Raised when an attempt to delete a zone which is still delegated to Akamai
|
|
is made
|
|
"""
|
|
error_type = 'delegation_exists'
|
|
|
|
|
|
class DuplicateDomain(exceptions.DuplicateDomain, EnhancedDNSException):
|
|
"""
|
|
Raised when an attempt to create a zone which is registered to another
|
|
Akamai account is made
|
|
"""
|
|
pass
|
|
|
|
|
|
class Forbidden(exceptions.Forbidden, EnhancedDNSException):
|
|
"""
|
|
Raised when an attempt to modify a zone which is registered to another
|
|
Akamai account is made.
|
|
|
|
This appears to be returned when creating a new subdomain of domain which
|
|
already exists in another Akamai account.
|
|
"""
|
|
pass
|
|
|
|
|
|
class EnhancedDNSHttpAuthenticated(HttpAuthenticated):
|
|
def addenhanceddnsheaders(self, request):
|
|
request.headers['Pragma'] = ('akamai-x-get-request-id, '
|
|
'akamai-x-cache-on, '
|
|
'akamai-x-cache-remote-on, '
|
|
'akamai-x-get-cache-key')
|
|
|
|
def logenhanceddnsheaders(self, response):
|
|
request_id = response.headers.get('x-akamai-request-id', '-')
|
|
cache = response.headers.get('x-cache', '-')
|
|
cache_key = response.headers.get('x-cache-key', '-')
|
|
cache_remote = response.headers.get('x-cache-remote', '-')
|
|
|
|
LOG.debug('Akamai Request-ID: %s, Cache-Key: %s, Cache: %s, '
|
|
'Cache-Remote: %s', request_id, cache_key, cache,
|
|
cache_remote)
|
|
|
|
def send(self, request):
|
|
self.addenhanceddnsheaders(request)
|
|
|
|
response = HttpAuthenticated.send(self, request)
|
|
|
|
self.logenhanceddnsheaders(response)
|
|
|
|
return response
|
|
|
|
|
|
class EnhancedDNSClient(object):
|
|
"""EnhancedDNS SOAP API Client"""
|
|
|
|
def __init__(self, username, password):
|
|
# Prepare a SUDS transport with the approperiate credentials
|
|
transport = EnhancedDNSHttpAuthenticated(
|
|
username=username,
|
|
password=password,
|
|
proxy=utils.get_proxies())
|
|
|
|
# Prepare a SUDS client
|
|
self.client = SudsClient(CONF['backend:akamai'].enhanceddns_wsdl,
|
|
transport=transport)
|
|
|
|
def buildZone(self, zoneName, masters, endCustomerId, tsigKeyName=None,
|
|
tsigKey=None, tsigAlgorithm=None):
|
|
zone = self.client.factory.create('ns3:Zone')
|
|
|
|
# Set some defaults
|
|
zone.transferMode = "axfr"
|
|
zone.type = "edns"
|
|
zone.notify = 1
|
|
zone.dnssec = False
|
|
|
|
# Set the remaining options
|
|
zone.zoneName = self._sanitizeZoneName(zoneName)
|
|
zone.masters = masters
|
|
zone.tsigKeyName = tsigKeyName
|
|
zone.tsigKey = tsigKey
|
|
zone.tsigAlgorithm = tsigAlgorithm
|
|
zone.endCustomerId = endCustomerId
|
|
|
|
return zone
|
|
|
|
def getZone(self, zoneName):
|
|
LOG.debug("Performing getZone with zoneName: %s" % zoneName)
|
|
zoneName = self._sanitizeZoneName(zoneName)
|
|
|
|
try:
|
|
return self.client.service.getZone(zoneName=zoneName)
|
|
except Exception as e:
|
|
raise EnhancedDNSException('Akamai Communication Failure: %s' % e)
|
|
|
|
def setZones(self, zones):
|
|
LOG.debug("Performing setZones")
|
|
try:
|
|
return self.client.service.setZones(zones=zones)
|
|
except Exception as e:
|
|
if 'You do not have permission to view this zone' in str(e):
|
|
raise DuplicateDomain()
|
|
elif 'You do not have access to edit this zone' in str(e):
|
|
raise Forbidden()
|
|
elif 'basic auth failed' in str(e):
|
|
raise exceptions.ConfigurationError(
|
|
'Invalid Akamai credentials')
|
|
else:
|
|
raise EnhancedDNSException('Akamai Communication Failure: %s'
|
|
% e)
|
|
|
|
def setZone(self, zone):
|
|
LOG.debug("Performing setZone with zoneName: %s" % zone.zoneName)
|
|
try:
|
|
self.client.service.setZone(zone=zone)
|
|
except Exception as e:
|
|
if 'You do not have permission to view this zone' in str(e):
|
|
raise DuplicateDomain()
|
|
elif 'You do not have access to edit this zone' in str(e):
|
|
raise Forbidden()
|
|
elif 'basic auth failed' in str(e):
|
|
raise exceptions.ConfigurationError(
|
|
'Invalid Akamai credentials')
|
|
else:
|
|
raise EnhancedDNSException('Akamai Communication Failure: %s'
|
|
% e)
|
|
|
|
def deleteZone(self, zoneName):
|
|
LOG.debug("Performing deleteZone with zoneName: %s", zoneName)
|
|
zoneName = self._sanitizeZoneName(zoneName)
|
|
|
|
self.deleteZones(zoneNames=[zoneName])
|
|
|
|
def deleteZones(self, zoneNames):
|
|
LOG.debug("Performing deleteZones with zoneNames: %r", zoneNames)
|
|
zoneNames = [self._sanitizeZoneName(zN) for zN in zoneNames]
|
|
|
|
try:
|
|
self.client.service.deleteZones(zoneNames=zoneNames)
|
|
except Exception as e:
|
|
if 'Could not retrive object ID for zone' in str(e):
|
|
# The zone has already been purged, ignore and move on
|
|
pass
|
|
elif 'The following zones are still delegated to Akamai' in str(e):
|
|
raise DelegationExists()
|
|
elif 'basic auth failed' in str(e):
|
|
raise exceptions.ConfigurationError(
|
|
'Invalid Akamai credentials')
|
|
else:
|
|
raise EnhancedDNSException('Akamai Communication Failure: %s'
|
|
% e)
|
|
|
|
def _sanitizeZoneName(self, zoneName):
|
|
return zoneName.rstrip('.').lower()
|
|
|
|
|
|
def build_zone(client, target, domain):
|
|
masters = [m.host for m in target.masters]
|
|
|
|
if target.options.get("tsig_key_name", None):
|
|
return client.buildZone(
|
|
domain.name,
|
|
masters,
|
|
domain.id,
|
|
target.options["tsig_key_name"],
|
|
target.options.get("tsig_key_secret", None),
|
|
target.options.get("tsig_key_algorithm", None))
|
|
else:
|
|
return client.buildZone(
|
|
domain.name,
|
|
masters,
|
|
domain.id)
|
|
|
|
|
|
class AkamaiBackend(base.Backend):
|
|
__plugin_name__ = 'akamai'
|
|
|
|
__backend_status__ = 'release-compatible'
|
|
|
|
@classmethod
|
|
def get_cfg_opts(cls):
|
|
group = cfg.OptGroup(
|
|
name='backend:akamai', title='Backend options for Akamai'
|
|
)
|
|
|
|
opts = [
|
|
cfg.StrOpt('enhanceddns_wsdl',
|
|
default='file://%s' % WSDL,
|
|
help='Akamai EnhancedDNS WSDL URL'),
|
|
]
|
|
|
|
return [(group, opts)]
|
|
|
|
def __init__(self, target):
|
|
super(AkamaiBackend, self).__init__(target)
|
|
|
|
self.username = self.options.get('username')
|
|
self.password = self.options.get('password')
|
|
|
|
self.client = EnhancedDNSClient(self.username, self.password)
|
|
|
|
for m in self.masters:
|
|
if m.port != 53:
|
|
raise exceptions.ConfigurationError(
|
|
"Akamai only supports mDNS instances on port 53")
|
|
|
|
def create_domain(self, context, domain):
|
|
"""Create a DNS domain"""
|
|
zone = build_zone(self.client, self.target, domain)
|
|
|
|
self.client.setZone(zone=zone)
|
|
|
|
def delete_domain(self, context, domain):
|
|
"""Delete a DNS domain"""
|
|
self.client.deleteZone(zoneName=domain['name'])
|