Source code for designate.backend.impl_akamai

# Copyright 2012-2015 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@hpe.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.
from oslo_log import log as logging
from oslo_config import cfg
from oslo_utils import importutils

from designate import exceptions
from designate import utils
from designate.backend import base


try:
    SudsClient = importutils.import_class("suds.client.Client")
    HttpAuthenticated = importutils.import_class(
        "suds.transport.https.HttpAuthenticated")

except ImportError:
    SudsClient = None

    class HttpAuthenticated(object):
        pass


LOG = logging.getLogger(__name__)
CONF = cfg.CONF


[docs]class EnhancedDNSException(exceptions.Backend): pass
[docs]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'
[docs]class DuplicateZone(exceptions.DuplicateZone, EnhancedDNSException): """ Raised when an attempt to create a zone which is registered to another Akamai account is made """ pass
[docs]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 subzone of zone which already exists in another Akamai account. """ pass
[docs]class EnhancedDNSHttpAuthenticated(HttpAuthenticated):
[docs] 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')
[docs] 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)
[docs] def send(self, request): self.addenhanceddnsheaders(request) response = HttpAuthenticated.send(self, request) self.logenhanceddnsheaders(response) return response
[docs]class EnhancedDNSClient(object): """EnhancedDNS SOAP API Client""" def __init__(self, username, password): # Ensure Suds (or suds-jerko) have been installed if SudsClient is None: raise EnhancedDNSException( "Dependency missing, please install suds or suds-jurko") # Prepare a SUDS transport with the appropriate 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)
[docs] 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
[docs] 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)
[docs] 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 DuplicateZone() 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)
[docs] 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 DuplicateZone() 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)
[docs] def deleteZone(self, zoneName): LOG.debug("Performing deleteZone with zoneName: %s", zoneName) zoneName = self._sanitizeZoneName(zoneName) self.deleteZones(zoneNames=[zoneName])
[docs] 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: # *READ THIS SECTION BEFORE MAKING ANY CHANGES* # Added 01/2017 by Graham Hayes. # If you have run a spell checking tool against the repo, and it # changes the line below - the patch will get -2'd. # This is matching a string that comes back from the akamai API. # If the akamai API changes - then this should change, but no # other reason. 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()
[docs]def build_zone(client, target, zone): masters = [m.host for m in target.masters] if target.options.get("tsig_key_name", None): return client.buildZone( zone.name, masters, zone.id, target.options.get("tsig_key_name", None), target.options.get("tsig_key_secret", None), target.options.get("tsig_key_algorithm", None)) else: return client.buildZone( zone.name, masters, zone.id)
[docs]class AkamaiBackend(base.Backend): __plugin_name__ = 'akamai' __backend_status__ = 'untested' 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")
[docs] def create_zone(self, context, zone): """Create a DNS zone""" zone = build_zone(self.client, self.target, zone) self.client.setZone(zone=zone)
[docs] def delete_zone(self, context, zone): """Delete a DNS zone""" self.client.deleteZone(zoneName=zone['name'])