FloatingIP PTR record functionality
* Central methods update_floatingip, get_floatingip, list_floatingip * API according to * https://wiki.openstack.org/wiki/Designate/Blueprints/Reverse * Put network functionanlity into a pluggable api class (In case we want * more) blueprint floating-ip-ptrs Change-Id: Ic9194e5bcfc8d25126b9e84652144f58cddcccfe
This commit is contained in:
parent
f78c67d07f
commit
6fd81240b7
90
designate/api/v2/controllers/floatingips.py
Normal file
90
designate/api/v2/controllers/floatingips.py
Normal file
@ -0,0 +1,90 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@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 pecan
|
||||
import re
|
||||
from designate import exceptions
|
||||
from designate import schema
|
||||
from designate.api.v2.controllers import rest
|
||||
from designate.api.v2.views import floatingips as floatingips_views
|
||||
from designate.central import rpcapi as central_rpcapi
|
||||
|
||||
|
||||
central_api = central_rpcapi.CentralAPI()
|
||||
|
||||
|
||||
FIP_REGEX = '^(?P<region>[A-Za-z0-9\\.\\-_]{1,100}):' \
|
||||
'(?P<id>[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-' \
|
||||
'[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$'
|
||||
|
||||
|
||||
def fip_key_to_data(key):
|
||||
m = re.match(FIP_REGEX, key)
|
||||
|
||||
# NOTE: Ensure that the fip matches region:floatingip_id or raise, if
|
||||
# not this will cause a 500.
|
||||
if m is None:
|
||||
msg = 'Floating IP %s is not in the format of <region>:<uuid>'
|
||||
raise exceptions.BadRequest(msg % key)
|
||||
return m.groups()
|
||||
|
||||
|
||||
class FloatingIPController(rest.RestController):
|
||||
_view = floatingips_views.FloatingIPView()
|
||||
_resource_schema = schema.Schema('v2', 'floatingip')
|
||||
_collection_schema = schema.Schema('v2', 'floatingips')
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
def get_all(self, **params):
|
||||
""" List Floating IP PTRs for a Tenant """
|
||||
request = pecan.request
|
||||
context = request.environ['context']
|
||||
|
||||
fips = central_api.list_floatingips(context)
|
||||
return self._view.list(context, request, fips)
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
def patch_one(self, fip_key):
|
||||
"""
|
||||
Set or unset a PTR
|
||||
"""
|
||||
request = pecan.request
|
||||
context = request.environ['context']
|
||||
body = request.body_dict
|
||||
|
||||
region, id_ = fip_key_to_data(fip_key)
|
||||
|
||||
# Validate the request conforms to the schema
|
||||
self._resource_schema.validate(body)
|
||||
|
||||
fip = central_api.update_floatingip(
|
||||
context, region, id_, body['floatingip'])
|
||||
|
||||
if fip:
|
||||
return self._view.basic(context, request, fip)
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
def get_one(self, fip_key):
|
||||
"""
|
||||
Get PTR
|
||||
"""
|
||||
request = pecan.request
|
||||
context = request.environ['context']
|
||||
|
||||
region, id_ = fip_key_to_data(fip_key)
|
||||
|
||||
fip = central_api.get_floatingip(context, region, id_)
|
||||
|
||||
return self._view.basic(context, request, fip)
|
21
designate/api/v2/controllers/reverse.py
Normal file
21
designate/api/v2/controllers/reverse.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@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.
|
||||
from designate.api.v2.controllers import rest
|
||||
from designate.api.v2.controllers import floatingips
|
||||
|
||||
|
||||
class ReverseController(rest.RestController):
|
||||
floatingips = floatingips.FloatingIPController()
|
@ -15,6 +15,7 @@
|
||||
# under the License.
|
||||
from designate.openstack.common import log as logging
|
||||
from designate.api.v2.controllers import limits
|
||||
from designate.api.v2.controllers import reverse
|
||||
from designate.api.v2.controllers import schemas
|
||||
from designate.api.v2.controllers import zones
|
||||
|
||||
@ -28,4 +29,5 @@ class RootController(object):
|
||||
"""
|
||||
limits = limits.LimitsController()
|
||||
schemas = schemas.SchemasController()
|
||||
reverse = reverse.ReverseController()
|
||||
zones = zones.ZonesController()
|
||||
|
36
designate/api/v2/views/floatingips.py
Normal file
36
designate/api/v2/views/floatingips.py
Normal file
@ -0,0 +1,36 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@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.
|
||||
from designate.api.v2.views import base as base_view
|
||||
from designate.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FloatingIPView(base_view.BaseView):
|
||||
""" Model a FloatingIP PTR record as a python dict """
|
||||
_resource_name = 'floatingip'
|
||||
_collection_name = 'floatingips'
|
||||
|
||||
def _get_base_href(self, parents=None):
|
||||
return '%s/reverse/floatingips' % self.base_uri
|
||||
|
||||
def basic(self, context, request, data):
|
||||
data['id'] = ":".join([data.pop('region'), data.pop('id')])
|
||||
data['links'] = self._get_resource_links(
|
||||
request, data, [data['id']])
|
||||
return {
|
||||
'floatingip': data}
|
@ -41,4 +41,8 @@ cfg.CONF.register_opts([
|
||||
cfg.IntOpt('max_recordset_name_len', default=255,
|
||||
help="Maximum recordset name length",
|
||||
deprecated_name='max_record_name_len'),
|
||||
cfg.StrOpt('managed_resource_email', default='email@example.io',
|
||||
help='E-Mail for Managed resources'),
|
||||
cfg.StrOpt('managed_resource_tenant_id',
|
||||
help="The Tenant ID that will own any managed resources.")
|
||||
], group='service:central')
|
||||
|
@ -33,7 +33,7 @@ class CentralAPI(rpc_proxy.RpcProxy):
|
||||
2.0 - Renamed most get_resources to find_resources
|
||||
2.1 - Add quota methods
|
||||
3.0 - RecordSet Changes
|
||||
|
||||
3.1 - Add floating ip ptr methods
|
||||
"""
|
||||
def __init__(self, topic=None):
|
||||
topic = topic if topic else cfg.CONF.central_topic
|
||||
@ -353,3 +353,17 @@ class CentralAPI(rpc_proxy.RpcProxy):
|
||||
record_id=record_id)
|
||||
|
||||
return self.call(context, msg)
|
||||
|
||||
def list_floatingips(self, context):
|
||||
msg = self.make_msg('list_floatingips')
|
||||
return self.call(context, msg, version="3.1")
|
||||
|
||||
def get_floatingip(self, context, region, floatingip_id):
|
||||
msg = self.make_msg('get_floatingip', region=region,
|
||||
floatingip_id=floatingip_id)
|
||||
return self.call(context, msg, version="3.1")
|
||||
|
||||
def update_floatingip(self, context, region, floatingip_id, values):
|
||||
msg = self.make_msg('update_floatingip', region=region,
|
||||
floatingip_id=floatingip_id, values=values)
|
||||
return self.call(context, msg)
|
||||
|
@ -27,6 +27,7 @@ from designate import policy
|
||||
from designate import quota
|
||||
from designate import utils
|
||||
from designate.storage import api as storage_api
|
||||
from designate import network_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -45,7 +46,7 @@ def wrap_backend_call():
|
||||
|
||||
|
||||
class Service(rpc_service.Service):
|
||||
RPC_API_VERSION = '3.0'
|
||||
RPC_API_VERSION = '3.1'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
backend_driver = cfg.CONF['service:central'].backend_driver
|
||||
@ -69,6 +70,8 @@ class Service(rpc_service.Service):
|
||||
self.quota = quota.get_quota()
|
||||
self.effective_tld = effectivetld.EffectiveTld()
|
||||
|
||||
self.network_api = network_api.get_api(cfg.CONF.network_api)
|
||||
|
||||
def start(self):
|
||||
self.backend.start()
|
||||
|
||||
@ -964,3 +967,295 @@ class Service(rpc_service.Service):
|
||||
'backend': backend_status,
|
||||
'storage': storage_status
|
||||
}
|
||||
|
||||
def _determine_floatingips(self, context, fips, records=None,
|
||||
tenant_id=None):
|
||||
"""
|
||||
Given the context or tenant, records and fips it returns the valid
|
||||
floatingips either with a associated record or not. Deletes invalid
|
||||
records also.
|
||||
|
||||
Returns a list of tuples with FloatingIPs and it's Record.
|
||||
"""
|
||||
tenant_id = tenant_id or context.tenant_id
|
||||
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
criterion = {
|
||||
'managed': True,
|
||||
'managed_resource_type': 'ptr:floatingip',
|
||||
}
|
||||
|
||||
records = self.find_records(elevated_context, criterion)
|
||||
records = dict([(r['managed_extra'], r) for r in records])
|
||||
|
||||
invalid = []
|
||||
data = {}
|
||||
# First populate the list of FIPS
|
||||
for fip_key, fip_values in fips.items():
|
||||
# Check if the FIP has a record
|
||||
record = records.get(fip_values['address'])
|
||||
|
||||
# NOTE: Now check if it's owned by the tenant that actually has the
|
||||
# FIP in the external service and if not invalidate it (delete it)
|
||||
# thus not returning it with in the tuple with the FIP, but None..
|
||||
|
||||
if record:
|
||||
record_tenant = record['managed_tenant_id']
|
||||
|
||||
if record_tenant != tenant_id:
|
||||
msg = "Invalid FloatingIP %s belongs to %s but record " \
|
||||
"owner %s"
|
||||
LOG.debug(msg, fip_key, tenant_id, record_tenant)
|
||||
|
||||
invalid.append(record)
|
||||
record = None
|
||||
data[fip_key] = (fip_values, record)
|
||||
|
||||
return data, invalid
|
||||
|
||||
def _invalidate_floatingips(self, context, records):
|
||||
"""
|
||||
Utility method to delete a list of records.
|
||||
"""
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
if records > 0:
|
||||
for r in records:
|
||||
msg = 'Deleting record %s for FIP %s'
|
||||
LOG.debug(msg, r['id'], r['managed_resource_id'])
|
||||
self.delete_record(elevated_context, r['domain_id'],
|
||||
r['recordset_id'], r['id'])
|
||||
|
||||
def _format_floatingips(self, context, data, recordsets=None):
|
||||
"""
|
||||
Given a list of FloatingIP and Record tuples we look through creating
|
||||
a new dict of FloatingIPs
|
||||
"""
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
fips = {}
|
||||
for key, value in data.items():
|
||||
fip_ptr = {
|
||||
'address': value[0]['address'],
|
||||
'id': value[0]['id'],
|
||||
'region': value[0]['region'],
|
||||
'ptrdname': None,
|
||||
'ttl': None,
|
||||
'description': None
|
||||
}
|
||||
|
||||
# TTL population requires a present record in order to find the
|
||||
# RS or Zone
|
||||
if value[1]:
|
||||
# We can have a recordset dict passed in
|
||||
if (recordsets is not None and
|
||||
value[1]['recordset_id'] in recordsets):
|
||||
recordset = recordsets[value[1]['recordset_id']]
|
||||
else:
|
||||
recordset = self.storage_api.get_recordset(
|
||||
elevated_context, value[1]['recordset_id'])
|
||||
|
||||
if recordset['ttl'] is not None:
|
||||
fip_ptr['ttl'] = recordset['ttl']
|
||||
else:
|
||||
zone = self.get_domain(
|
||||
elevated_context, value[1]['domain_id'])
|
||||
fip_ptr['ttl'] = zone['ttl']
|
||||
|
||||
fip_ptr['ptrdname'] = value[1]['data']
|
||||
else:
|
||||
LOG.debug("No record information found for %s",
|
||||
value[0]['id'])
|
||||
|
||||
# Store the "fip_record" with the region and it's id as key
|
||||
fips[key] = fip_ptr
|
||||
return fips
|
||||
|
||||
def _list_floatingips(self, context, region=None):
|
||||
data = self.network_api.list_floatingips(context, region=region)
|
||||
return self._list_to_dict(data, keys=['region', 'id'])
|
||||
|
||||
def _list_to_dict(self, data, keys=['id']):
|
||||
new = {}
|
||||
for i in data:
|
||||
key = tuple([i[key] for key in keys])
|
||||
new[key] = i
|
||||
return new
|
||||
|
||||
def _get_floatingip(self, context, region, floatingip_id, fips):
|
||||
if (region, floatingip_id) not in fips:
|
||||
msg = 'FloatingIP %s in %s is not associated for tenant "%s"' % \
|
||||
(floatingip_id, region, context.tenant_id)
|
||||
raise exceptions.NotFound(msg)
|
||||
return fips[region, floatingip_id]
|
||||
|
||||
# PTR ops
|
||||
def list_floatingips(self, context):
|
||||
"""
|
||||
List Floating IPs PTR
|
||||
|
||||
A) We have service_catalog in the context and do a lookup using the
|
||||
token pr Neutron in the SC
|
||||
B) We lookup FIPs using the configured values for this deployment.
|
||||
"""
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
tenant_fips = self._list_floatingips(context)
|
||||
|
||||
valid, invalid = self._determine_floatingips(
|
||||
elevated_context, tenant_fips)
|
||||
|
||||
self._invalidate_floatingips(context, invalid)
|
||||
|
||||
return self._format_floatingips(context, valid).values()
|
||||
|
||||
def get_floatingip(self, context, region, floatingip_id):
|
||||
"""
|
||||
Get Floating IP PTR
|
||||
"""
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
tenant_fips = self._list_floatingips(context, region=region)
|
||||
|
||||
self._get_floatingip(context, region, floatingip_id, tenant_fips)
|
||||
|
||||
valid, invalid = self._determine_floatingips(
|
||||
elevated_context, tenant_fips)
|
||||
|
||||
self._invalidate_floatingips(context, invalid)
|
||||
|
||||
mangled = self._format_floatingips(context, valid)
|
||||
return mangled[region, floatingip_id]
|
||||
|
||||
def _set_floatingip_reverse(self, context, region, floatingip_id, values):
|
||||
"""
|
||||
Set the FloatingIP's PTR record based on values.
|
||||
"""
|
||||
values.setdefault('description', None)
|
||||
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
tenant_fips = self._list_floatingips(context, region=region)
|
||||
|
||||
fip = self._get_floatingip(context, region, floatingip_id, tenant_fips)
|
||||
|
||||
zone_name = self.network_api.address_zone(fip['address'])
|
||||
|
||||
# NOTE: Find existing zone or create it..
|
||||
try:
|
||||
zone = self.storage_api.find_domain(
|
||||
elevated_context, {'name': zone_name})
|
||||
except exceptions.DomainNotFound:
|
||||
msg = 'Creating zone for %s:%s - %s zone %s' % \
|
||||
(floatingip_id, region, fip['address'], zone_name)
|
||||
LOG.info(msg)
|
||||
|
||||
email = cfg.CONF['service:central'].managed_resource_email
|
||||
tenant_id = cfg.CONF['service:central'].managed_resource_tenant_id
|
||||
|
||||
zone_values = {
|
||||
'name': zone_name,
|
||||
'email': email,
|
||||
'tenant_id': tenant_id
|
||||
}
|
||||
|
||||
zone = self.create_domain(elevated_context, zone_values)
|
||||
|
||||
record_name = self.network_api.address_name(fip['address'])
|
||||
|
||||
try:
|
||||
# NOTE: Delete the current recormdset if any (also purges records)
|
||||
LOG.debug("Removing old RRset / Record")
|
||||
rset = self.find_recordset(
|
||||
elevated_context, {'name': record_name, 'type': 'PTR'})
|
||||
|
||||
records = self.find_records(
|
||||
elevated_context, {'recordset_id': rset['id']})
|
||||
|
||||
for record in records:
|
||||
self.delete_record(
|
||||
elevated_context,
|
||||
rset['domain_id'],
|
||||
rset['id'],
|
||||
record['id'])
|
||||
self.delete_recordset(elevated_context, zone['id'], rset['id'])
|
||||
except exceptions.RecordSetNotFound:
|
||||
pass
|
||||
|
||||
recordset_values = {
|
||||
'name': record_name,
|
||||
'type': 'PTR',
|
||||
'ttl': values.get('ttl', None)
|
||||
}
|
||||
|
||||
recordset = self.create_recordset(
|
||||
elevated_context, zone['id'], recordset_values)
|
||||
|
||||
record_values = {
|
||||
'data': values['ptrdname'],
|
||||
'description': values['description'],
|
||||
'type': 'PTR',
|
||||
'managed': True,
|
||||
'managed_extra': fip['address'],
|
||||
'managed_resource_id': floatingip_id,
|
||||
'managed_resource_region': region,
|
||||
'managed_resource_type': 'ptr:floatingip',
|
||||
'managed_tenant_id': context.tenant_id
|
||||
}
|
||||
|
||||
record = self.create_record(
|
||||
elevated_context, zone['id'], recordset['id'], record_values)
|
||||
|
||||
mangled = self._format_floatingips(
|
||||
context, {(region, floatingip_id): (fip, record)},
|
||||
{recordset['id']: recordset})
|
||||
|
||||
return mangled[region, floatingip_id]
|
||||
|
||||
def _unset_floatingip_reverse(self, context, region, floatingip_id):
|
||||
"""
|
||||
Unset the FloatingIP PTR record based on the
|
||||
|
||||
Service's FloatingIP ID > managed_resource_id
|
||||
Tenant ID > managed_tenant_id
|
||||
|
||||
We find the record based on the criteria and delete it or raise.
|
||||
"""
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
criterion = {
|
||||
'managed_resource_id': floatingip_id,
|
||||
'managed_tenant_id': context.tenant_id
|
||||
}
|
||||
|
||||
try:
|
||||
record = self.storage_api.find_record(
|
||||
elevated_context, criterion=criterion)
|
||||
except exceptions.RecordNotFound:
|
||||
msg = 'No such FloatingIP %s:%s' % (region, floatingip_id)
|
||||
raise exceptions.NotFound(msg)
|
||||
|
||||
self.delete_record(
|
||||
elevated_context,
|
||||
record['domain_id'],
|
||||
record['recordset_id'],
|
||||
record['id'])
|
||||
|
||||
def update_floatingip(self, context, region, floatingip_id, values):
|
||||
"""
|
||||
We strictly see if values['ptrdname'] is str or None and set / unset
|
||||
the requested FloatingIP's PTR record based on that.
|
||||
"""
|
||||
if values['ptrdname'] is None:
|
||||
self._unset_floatingip_reverse(context, region, floatingip_id)
|
||||
elif isinstance(values['ptrdname'], basestring):
|
||||
return self._set_floatingip_reverse(
|
||||
context, region, floatingip_id, values)
|
||||
|
@ -46,6 +46,18 @@ class ConfigurationError(Base):
|
||||
error_type = 'configuration_error'
|
||||
|
||||
|
||||
class CommunicationFailure(Base):
|
||||
error_code = 504
|
||||
error_type = 'communication_failure'
|
||||
|
||||
|
||||
class NeutronCommunicationFailure(CommunicationFailure):
|
||||
"""
|
||||
Raised in case one of the alledged Neutron endpoints fails.
|
||||
"""
|
||||
error_type = 'neutron_communication_failure'
|
||||
|
||||
|
||||
class NoServersConfigured(ConfigurationError):
|
||||
error_code = 500
|
||||
error_type = 'no_servers_configured'
|
||||
@ -70,6 +82,11 @@ class BadRequest(Base):
|
||||
error_type = 'bad_request'
|
||||
|
||||
|
||||
class NetworkEndpointNotFound(BadRequest):
|
||||
error_type = 'no_endpoint'
|
||||
error_code = 403
|
||||
|
||||
|
||||
class InvalidOperation(BadRequest):
|
||||
error_code = 400
|
||||
error_type = 'invalid_operation'
|
||||
|
115
designate/network_api/__init__.py
Normal file
115
designate/network_api/__init__.py
Normal file
@ -0,0 +1,115 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@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.
|
||||
from dns import reversename
|
||||
from oslo.config import cfg
|
||||
from stevedore import driver
|
||||
|
||||
from designate import exceptions
|
||||
from designate.openstack.common import log as logging
|
||||
|
||||
|
||||
cfg.CONF.register_opts([
|
||||
cfg.StrOpt('network_api', default='neutron', help='Which API to use.')
|
||||
])
|
||||
|
||||
|
||||
LOG = logging.getLogger()
|
||||
|
||||
|
||||
def get_api(api):
|
||||
mngr = driver.DriverManager('designate.network_api', api)
|
||||
return mngr.driver()
|
||||
|
||||
|
||||
class BaseAPI(object):
|
||||
"""
|
||||
Base API
|
||||
"""
|
||||
def _endpoints(self, service_catalog=None, service_type=None,
|
||||
endpoint_type='publicURL', config_section=None,
|
||||
region=None):
|
||||
if service_catalog is not None:
|
||||
endpoints = self._endpoints_from_catalog(
|
||||
service_catalog, service_type, endpoint_type,
|
||||
region=region)
|
||||
elif config_section is not None:
|
||||
endpoints = []
|
||||
for u in cfg.CONF[config_section].endpoints:
|
||||
e_region, e = u.split('|')
|
||||
# Filter if region is given
|
||||
if (e_region and region) and e_region != region:
|
||||
continue
|
||||
endpoints.append((e, e_region))
|
||||
|
||||
if not endpoints:
|
||||
msg = 'Endpoints are not configured'
|
||||
raise exceptions.ConfigurationError(msg)
|
||||
else:
|
||||
msg = 'No service_catalog and no configured endpoints'
|
||||
raise exceptions.ConfigurationError(msg)
|
||||
|
||||
LOG.debug('Returning endpoints: %s' % endpoints)
|
||||
return endpoints
|
||||
|
||||
def _endpoints_from_catalog(self, service_catalog, service_type,
|
||||
endpoint_type, region=None):
|
||||
"""
|
||||
Return the endpoints for the given service from the context's sc
|
||||
or lookup towards the configured keystone.
|
||||
|
||||
return [('http://endpoint', 'region')]
|
||||
"""
|
||||
urls = []
|
||||
for svc in service_catalog:
|
||||
if svc['type'] != service_type:
|
||||
continue
|
||||
for url in svc['endpoints']:
|
||||
if endpoint_type in url:
|
||||
if region is not None and url['region'] != region:
|
||||
continue
|
||||
urls.append((url[endpoint_type], url['region']))
|
||||
if not urls:
|
||||
raise exceptions.NetworkEndpointNotFound
|
||||
return urls
|
||||
|
||||
def list_floatingips(self, context, region=None):
|
||||
"""
|
||||
List Floating IPs.
|
||||
|
||||
Should return something like:
|
||||
|
||||
[{
|
||||
'address': '<ip address'>,
|
||||
'region': '<region where this belongs>',
|
||||
'id': '<id of the FIP>'
|
||||
}]
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def address_zone(address):
|
||||
"""
|
||||
Get the zone a address belongs to.
|
||||
"""
|
||||
parts = reversed(address.split('.')[:-1])
|
||||
return '%s.in-addr.arpa.' % ".".join(parts)
|
||||
|
||||
@staticmethod
|
||||
def address_name(address):
|
||||
"""
|
||||
Get the name for the address
|
||||
"""
|
||||
return reversename.from_address(address).to_text()
|
80
designate/network_api/fake.py
Normal file
80
designate/network_api/fake.py
Normal file
@ -0,0 +1,80 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@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 uuid
|
||||
from designate.openstack.common import log
|
||||
from designate.network_api import BaseAPI
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
POOL = dict([(str(uuid.uuid4()), '192.168.2.%s' % i) for i in xrange(0, 254)])
|
||||
ALLOCATIONS = {}
|
||||
|
||||
|
||||
def _format_floatingip(id_, address):
|
||||
return {
|
||||
'region': 'RegionOne',
|
||||
'address': address,
|
||||
'id': id_
|
||||
}
|
||||
|
||||
|
||||
def allocate_floatingip(tenant_id, floatingip_id=None):
|
||||
"""
|
||||
Allocates a floating ip from the pool to the tenant.
|
||||
"""
|
||||
ALLOCATIONS.setdefault(tenant_id, {})
|
||||
|
||||
id_ = floatingip_id or POOL.keys()[0]
|
||||
|
||||
ALLOCATIONS[tenant_id][id_] = POOL.pop(id_)
|
||||
values = _format_floatingip(id_, ALLOCATIONS[tenant_id][id_])
|
||||
LOG.debug("Allocated to id_ %s to %s - %s", id_, tenant_id, values)
|
||||
return values
|
||||
|
||||
|
||||
def deallocate_floatingip(id_):
|
||||
"""
|
||||
Deallocate a floatingip
|
||||
"""
|
||||
LOG.debug('De-allocating %s' % id_)
|
||||
for tenant_id, allocated in ALLOCATIONS.items():
|
||||
if id_ in allocated:
|
||||
POOL[id_] = allocated.pop(id_)
|
||||
break
|
||||
else:
|
||||
raise KeyError('No such FloatingIP %s' % id_)
|
||||
|
||||
|
||||
def reset_floatingips():
|
||||
LOG.debug('Resetting any allocations.')
|
||||
for tenant_id, allocated in ALLOCATIONS.items():
|
||||
for key, value in allocated.items():
|
||||
POOL[key] = allocated.pop(key)
|
||||
|
||||
|
||||
class API(BaseAPI):
|
||||
def list_floatingips(self, context, region=None):
|
||||
if context.is_admin:
|
||||
data = []
|
||||
for tenant_id, allocated in ALLOCATIONS.items():
|
||||
data.extend(allocated.items())
|
||||
else:
|
||||
data = ALLOCATIONS.get(context.tenant_id, {}).items()
|
||||
|
||||
formatted = [_format_floatingip(k, v) for k, v in data]
|
||||
LOG.debug('Returning %i FloatingIPs: %s', len(formatted), formatted)
|
||||
return formatted
|
148
designate/network_api/neutron.py
Normal file
148
designate/network_api/neutron.py
Normal file
@ -0,0 +1,148 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Copied partially from nova
|
||||
|
||||
from neutronclient.v2_0 import client as clientv20
|
||||
from neutronclient.common import exceptions as neutron_exceptions
|
||||
from oslo.config import cfg
|
||||
|
||||
from designate import exceptions
|
||||
|
||||
from designate.openstack.common import log as logging
|
||||
from designate.openstack.common import threadgroup
|
||||
from designate.network_api import BaseAPI
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
neutron_opts = [
|
||||
cfg.ListOpt('endpoints',
|
||||
help='URL to use if None in the ServiceCatalog that is '
|
||||
'passed by the requrest context. Format: <region>|<url>'),
|
||||
cfg.StrOpt('endpoint_type', default='publicURL',
|
||||
help="Endpoint type to use"),
|
||||
cfg.IntOpt('timeout',
|
||||
default=30,
|
||||
help='timeout value for connecting to neutron in seconds'),
|
||||
cfg.StrOpt('admin_username',
|
||||
help='username for connecting to neutron in admin context'),
|
||||
cfg.StrOpt('admin_password',
|
||||
help='password for connecting to neutron in admin context',
|
||||
secret=True),
|
||||
cfg.StrOpt('admin_tenant_name',
|
||||
help='tenant name for connecting to neutron in admin context'),
|
||||
cfg.StrOpt('auth_url',
|
||||
help='auth url for connecting to neutron in admin context'),
|
||||
cfg.BoolOpt('insecure',
|
||||
default=False,
|
||||
help='if set, ignore any SSL validation issues'),
|
||||
cfg.StrOpt('auth_strategy',
|
||||
default='keystone',
|
||||
help='auth strategy for connecting to '
|
||||
'neutron in admin context'),
|
||||
cfg.StrOpt('ca_certificates_file',
|
||||
help='Location of ca certificates file to use for '
|
||||
'neutron client requests.'),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(neutron_opts, group='network_api:neutron')
|
||||
|
||||
|
||||
def get_client(context, endpoint):
|
||||
params = {
|
||||
'endpoint_url': endpoint,
|
||||
'timeout': CONF['network_api:neutron'].timeout,
|
||||
'insecure': CONF['network_api:neutron'].insecure,
|
||||
'ca_cert': CONF['network_api:neutron'].ca_certificates_file,
|
||||
}
|
||||
|
||||
if context.auth_token:
|
||||
params['token'] = context.auth_token
|
||||
params['auth_strategy'] = None
|
||||
elif CONF['network_api:neutron'].admin_username is not None:
|
||||
params['username'] = CONF['network_api:neutron'].admin_username
|
||||
params['tenant_name'] = CONF['network_api:neutron'].admin_tenant_name
|
||||
params['password'] = CONF['network_api:neutron'].admin_password
|
||||
params['auth_url'] = CONF['network_api:neutron'].admin_auth_url
|
||||
params['auth_strategy'] = CONF['network_api:neutron'].auth_strategy
|
||||
return clientv20.Client(**params)
|
||||
|
||||
|
||||
class API(BaseAPI):
|
||||
"""
|
||||
Interact with the Neutron API
|
||||
"""
|
||||
def list_floatingips(self, context, region=None):
|
||||
"""
|
||||
Get floating ips based on the current context from Neutron
|
||||
"""
|
||||
endpoints = self._endpoints(
|
||||
service_catalog=context.service_catalog,
|
||||
service_type='network',
|
||||
endpoint_type=CONF['network_api:neutron'].endpoint_type,
|
||||
config_section='network_api:neutron',
|
||||
region=region)
|
||||
|
||||
tg = threadgroup.ThreadGroup()
|
||||
|
||||
failed = []
|
||||
data = []
|
||||
|
||||
def _call(endpoint, region, *args, **kw):
|
||||
client = get_client(context, endpoint=endpoint)
|
||||
LOG.debug("Attempting to fetch FloatingIPs from %s @ %s",
|
||||
endpoint, region)
|
||||
try:
|
||||
fips = client.list_floatingips(*args, **kw)
|
||||
except neutron_exceptions.Unauthorized as e:
|
||||
# NOTE: 401 might be that the user doesn't have neutron
|
||||
# activated in a particular region, we'll just log the failure
|
||||
# and go on with our lives.
|
||||
msg = "Calling Neutron resulted in a 401, please investigate."
|
||||
LOG.warning(msg)
|
||||
LOG.exception(e)
|
||||
return
|
||||
except Exception as e:
|
||||
LOG.error('Failed calling Neutron %s - %s', region, endpoint)
|
||||
LOG.exception(e)
|
||||
failed.append((e, endpoint, region))
|
||||
return
|
||||
|
||||
for fip in fips['floatingips']:
|
||||
data.append({
|
||||
'id': fip['id'],
|
||||
'address': fip['floating_ip_address'],
|
||||
'region': region
|
||||
})
|
||||
|
||||
LOG.debug("Added %i FloatingIPs from %s @ %s", len(data),
|
||||
endpoint, region)
|
||||
|
||||
for endpoint, region in endpoints:
|
||||
tg.add_thread(_call, endpoint, region,
|
||||
tenant_id=context.tenant_id)
|
||||
tg.wait()
|
||||
|
||||
# NOTE: Sadly tg code doesn't give us a good way to handle failures.
|
||||
if failed:
|
||||
msg = 'Failed retrieving FLoatingIPs from Neutron in %s' % \
|
||||
", ".join(['%s - %s' % (i[1], i[2]) for i in failed])
|
||||
raise exceptions.NeutronCommunicationFailure(msg)
|
||||
return data
|
54
designate/resources/schemas/v2/floatingip.json
Normal file
54
designate/resources/schemas/v2/floatingip.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/hyper-schema",
|
||||
|
||||
"id": "floatingip",
|
||||
|
||||
"title": "floatingip",
|
||||
"description": "Floating IP PTR",
|
||||
"additionalProperties": false,
|
||||
|
||||
"required": ["floatingip"],
|
||||
|
||||
"properties": {
|
||||
"floatingip": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Floating IP PTR identifier",
|
||||
"pattern": "^[A-Za-z0-9\\.\\-_]{1,100}:([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$",
|
||||
"readOnly": true
|
||||
},
|
||||
"ptrdname": {
|
||||
"type": ["string", "null"],
|
||||
"format": "hostname",
|
||||
"required:": true
|
||||
},
|
||||
"description": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Description for the PTR",
|
||||
"maxLength": 160
|
||||
},
|
||||
"ttl": {
|
||||
"type": "integer",
|
||||
"description": "Default time to live",
|
||||
"min": 0,
|
||||
"max": 2147483647
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"self": {
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
designate/resources/schemas/v2/floatingips.json
Normal file
38
designate/resources/schemas/v2/floatingips.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/hyper-schema",
|
||||
|
||||
"id": "floatingips",
|
||||
|
||||
"title": "floatingips",
|
||||
"description": "Floating IP PTRs",
|
||||
"additionalProperties": false,
|
||||
|
||||
"required": ["floatingips"],
|
||||
|
||||
"properties": {
|
||||
"recordsets": {
|
||||
"type": "array",
|
||||
"description": "Floating IP",
|
||||
"items": {"$ref": "floatingips#/properties/flaotingip"}
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"self": {
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"next": {
|
||||
"type": ["string", "null"],
|
||||
"format": "url"
|
||||
},
|
||||
"previous": {
|
||||
"type": ["string", "null"],
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@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.
|
||||
#
|
||||
# This is a placeholder for Havana backports.
|
||||
# Do not use this number for new Icehouse work. New Icehouse work starts after
|
||||
# all the placeholders.
|
||||
#
|
||||
# See https://blueprints.launchpad.net/nova/+spec/backportable-db-migrations
|
||||
# http://lists.openstack.org/pipermail/openstack-dev/2013-March/006827.html
|
||||
from designate.openstack.common import log as logging
|
||||
from sqlalchemy import MetaData, Table, Column, Unicode
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
meta = MetaData()
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
records_table = Table('records', meta, autoload=True)
|
||||
record_managed_tenant_id = Column(
|
||||
'managed_tenant_id', Unicode(36), default=None, nullable=True)
|
||||
record_managed_tenant_id.create(records_table, populate_default=True)
|
||||
|
||||
record_managed_resource_region = Column(
|
||||
'managed_resource_region', Unicode(100), default=None, nullable=True)
|
||||
record_managed_resource_region.create(records_table, populate_default=True)
|
||||
|
||||
record_managed_extra = Column(
|
||||
'managed_extra', Unicode(100), default=None, nullable=True)
|
||||
record_managed_extra.create(records_table, populate_default=True)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
records_table = Table('records', meta, autoload=True)
|
||||
|
||||
record_managed_tenant_id = Column(
|
||||
'managed_tenant_id', Unicode(36), default=None, nullable=True)
|
||||
record_managed_tenant_id.drop(records_table)
|
||||
|
||||
record_managed_resource_region = Column(
|
||||
'managed_resource_region', Unicode(100), default=None, nullable=True)
|
||||
record_managed_resource_region.drop(records_table)
|
||||
|
||||
record_extra = Column(
|
||||
'managed_extra', Unicode(100), default=None, nullable=True)
|
||||
record_extra.drop(records_table)
|
@ -143,10 +143,13 @@ class Record(Base):
|
||||
hash = Column(String(32), nullable=False, unique=True)
|
||||
|
||||
managed = Column(Boolean, default=False)
|
||||
managed_extra = Column(Unicode(100), default=None, nullable=True)
|
||||
managed_plugin_type = Column(Unicode(50), default=None, nullable=True)
|
||||
managed_plugin_name = Column(Unicode(50), default=None, nullable=True)
|
||||
managed_resource_type = Column(Unicode(50), default=None, nullable=True)
|
||||
managed_resource_region = Column(Unicode(100), default=None, nullable=True)
|
||||
managed_resource_id = Column(UUID, default=None, nullable=True)
|
||||
managed_tenant_id = Column(Unicode(36), default=None, nullable=True)
|
||||
status = Column(Enum(name='resource_statuses', *RESOURCE_STATUSES),
|
||||
nullable=False, server_default='ACTIVE',
|
||||
default='ACTIVE')
|
||||
|
@ -34,6 +34,8 @@ from designate.openstack.common import uuidutils
|
||||
from designate.context import DesignateContext
|
||||
from designate.tests import resources
|
||||
from designate import exceptions
|
||||
from designate.network_api import fake as fake_network_api
|
||||
from designate import network_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -117,6 +119,14 @@ class DatabaseFixture(fixtures.Fixture):
|
||||
shutil.copyfile(self.golden_db, self.working_copy)
|
||||
|
||||
|
||||
class NetworkAPIFixture(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(NetworkAPIFixture, self).setUp()
|
||||
self.api = network_api.get_api(cfg.CONF.network_api)
|
||||
self.fake = fake_network_api
|
||||
self.addCleanup(self.fake.reset_floatingips)
|
||||
|
||||
|
||||
class TestCase(test.BaseTestCase):
|
||||
quota_fixtures = [{
|
||||
'resource': 'domains',
|
||||
@ -184,6 +194,11 @@ class TestCase(test.BaseTestCase):
|
||||
]
|
||||
}
|
||||
|
||||
ptr_fixtures = [
|
||||
{'ptrdname': 'srv1.example.com.'},
|
||||
{'ptrdname': 'srv1.example.net.'}
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
|
||||
@ -226,6 +241,11 @@ class TestCase(test.BaseTestCase):
|
||||
group='storage:sqlalchemy'
|
||||
)
|
||||
|
||||
self.config(network_api='fake')
|
||||
self.config(
|
||||
managed_resource_tenant_id='managing_tenant',
|
||||
group='service:central')
|
||||
|
||||
self.CONF([], project='designate')
|
||||
|
||||
self.notifications = NotifierFixture()
|
||||
@ -233,6 +253,9 @@ class TestCase(test.BaseTestCase):
|
||||
|
||||
self.useFixture(PolicyFixture())
|
||||
|
||||
self.network_api = NetworkAPIFixture()
|
||||
self.useFixture(self.network_api)
|
||||
|
||||
self.admin_context = self.get_admin_context()
|
||||
|
||||
# Config Methods
|
||||
@ -316,6 +339,11 @@ class TestCase(test.BaseTestCase):
|
||||
_values.update(values)
|
||||
return _values
|
||||
|
||||
def get_ptr_fixture(self, fixture=0, values={}):
|
||||
_values = copy.copy(self.ptr_fixtures[fixture])
|
||||
_values.update(values)
|
||||
return _values
|
||||
|
||||
def get_zonefile_fixture(self, variant=None):
|
||||
if variant is None:
|
||||
f = 'example.com.zone'
|
||||
|
250
designate/tests/test_api/test_v2/test_floatingips.py
Normal file
250
designate/tests/test_api/test_v2/test_floatingips.py
Normal file
@ -0,0 +1,250 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@managedit.ie>
|
||||
#
|
||||
# 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 designate.tests.test_api.test_v2 import ApiV2TestCase
|
||||
|
||||
|
||||
"""
|
||||
NOTE: Record invalidation is tested in Central tests
|
||||
"""
|
||||
|
||||
|
||||
class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
|
||||
def test_get_floatingip_no_record(self):
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant)
|
||||
|
||||
response = self.client.get(
|
||||
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
|
||||
headers={'X-Test-Tenant-Id': context.tenant_id})
|
||||
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertIn('floatingip', response.json)
|
||||
|
||||
#TODO(ekarlso): Remove the floatingip key - bug in v2 api
|
||||
fip_record = response.json['floatingip']
|
||||
self.assertEqual(":".join([fip['region'],
|
||||
fip['id']]), fip_record['id'])
|
||||
self.assertEqual(fip['address'], fip_record['address'])
|
||||
self.assertEqual(None, fip_record['description'])
|
||||
self.assertEqual(None, fip_record['ptrdname'])
|
||||
|
||||
def test_get_floatingip_with_record(self):
|
||||
self.create_server()
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(
|
||||
context.tenant)
|
||||
|
||||
self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
response = self.client.get(
|
||||
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
|
||||
headers={'X-Test-Tenant-Id': context.tenant_id})
|
||||
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertIn('floatingip', response.json)
|
||||
|
||||
# TODO(ekarlso): Remove the floatingip key - bug in v2 api
|
||||
fip_record = response.json['floatingip']
|
||||
self.assertEqual(":".join([fip['region'], fip['id']]),
|
||||
fip_record['id'])
|
||||
self.assertEqual(fip['address'], fip_record['address'])
|
||||
self.assertEqual(None, fip_record['description'])
|
||||
self.assertEqual(fixture['ptrdname'], fip_record['ptrdname'])
|
||||
|
||||
def test_get_floatingip_not_allocated(self):
|
||||
url = '/reverse/floatingips/foo:04580c52-b253-4eb7-8791-fbb9de9f856f'
|
||||
response = self.client.get(url, status=404)
|
||||
|
||||
self.assertIn('request_id', response.json)
|
||||
self.assertEqual(404, response.json['code'])
|
||||
self.assertEqual('not_found', response.json['type'])
|
||||
|
||||
def test_get_floatingip_invalid_key(self):
|
||||
response = self.client.get('/reverse/floatingips/foo:bar', status=400)
|
||||
|
||||
self.assertIn('message', response.json)
|
||||
self.assertIn('request_id', response.json)
|
||||
self.assertEqual(400, response.json['code'])
|
||||
self.assertEqual('bad_request', response.json['type'])
|
||||
|
||||
def test_list_floatingip_no_allocations(self):
|
||||
response = self.client.get('/reverse/floatingips')
|
||||
|
||||
self.assertIn('floatingips', response.json)
|
||||
self.assertIn('links', response.json)
|
||||
self.assertEqual(0, len(response.json['floatingips']))
|
||||
|
||||
def test_list_floatingip_no_record(self):
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
response = self.client.get(
|
||||
'/reverse/floatingips',
|
||||
headers={'X-Test-Tenant-Id': context.tenant_id})
|
||||
|
||||
self.assertIn('floatingips', response.json)
|
||||
self.assertIn('links', response.json)
|
||||
self.assertEqual(1, len(response.json['floatingips']))
|
||||
|
||||
#TODO(ekarlso): Remove the floatingip key - bug in v2 api
|
||||
fip_record = response.json['floatingips'][0]['floatingip']
|
||||
self.assertEqual(None, fip_record['ptrdname'])
|
||||
self.assertEqual(":".join([fip['region'], fip['id']]),
|
||||
fip_record['id'])
|
||||
self.assertEqual(fip['address'], fip_record['address'])
|
||||
self.assertEqual(None, fip_record['description'])
|
||||
|
||||
def test_list_floatingip_with_record(self):
|
||||
self.create_server()
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
response = self.client.get(
|
||||
'/reverse/floatingips',
|
||||
headers={'X-Test-Tenant-Id': context.tenant_id})
|
||||
|
||||
self.assertIn('floatingips', response.json)
|
||||
self.assertIn('links', response.json)
|
||||
self.assertEqual(1, len(response.json['floatingips']))
|
||||
|
||||
#TODO(ekarlso): Remove the floatingip key - bug in v2 api
|
||||
fip_record = response.json['floatingips'][0]['floatingip']
|
||||
self.assertEqual(fixture['ptrdname'], fip_record['ptrdname'])
|
||||
self.assertEqual(":".join([fip['region'], fip['id']]),
|
||||
fip_record['id'])
|
||||
self.assertEqual(fip['address'], fip_record['address'])
|
||||
self.assertEqual(None, fip_record['description'])
|
||||
self.assertEqual(fixture['ptrdname'], fip_record['ptrdname'])
|
||||
|
||||
def test_set_floatingip(self):
|
||||
self.create_server()
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip('tenant')
|
||||
|
||||
response = self.client.patch_json(
|
||||
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
|
||||
{"floatingip": fixture},
|
||||
headers={'X-Test-Tenant-Id': 'tenant'})
|
||||
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertIn('floatingip', response.json)
|
||||
|
||||
fip_record = response.json['floatingip']
|
||||
self.assertEqual(":".join([fip['region'], fip['id']]),
|
||||
fip_record['id'])
|
||||
self.assertEqual(fip['address'], fip_record['address'])
|
||||
self.assertEqual(None, fip_record['description'])
|
||||
self.assertEqual(fixture['ptrdname'], fip_record['ptrdname'])
|
||||
|
||||
def test_set_floatingip_not_allocated(self):
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip('tenant')
|
||||
self.network_api.fake.deallocate_floatingip(fip['id'])
|
||||
|
||||
response = self.client.patch_json(
|
||||
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
|
||||
{"floatingip": fixture}, status=404)
|
||||
|
||||
self.assertIn('message', response.json)
|
||||
self.assertIn('request_id', response.json)
|
||||
self.assertEqual(404, response.json['code'])
|
||||
self.assertEqual('not_found', response.json['type'])
|
||||
|
||||
def test_set_floatingip_invalid_ptrdname(self):
|
||||
fip = self.network_api.fake.allocate_floatingip('tenant')
|
||||
|
||||
response = self.client.patch_json(
|
||||
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
|
||||
{"floatingip": {'ptrxname': 'test'}}, status=400)
|
||||
|
||||
self.assertIn('message', response.json)
|
||||
self.assertIn('request_id', response.json)
|
||||
self.assertEqual(400, response.json['code'])
|
||||
self.assertEqual('invalid_object', response.json['type'])
|
||||
|
||||
def test_set_floatingip_invalid_key(self):
|
||||
response = self.client.patch_json(
|
||||
'/reverse/floatingips/%s' % 'foo:random', {}, status=400)
|
||||
|
||||
self.assertIn('message', response.json)
|
||||
self.assertIn('request_id', response.json)
|
||||
self.assertEqual(400, response.json['code'])
|
||||
self.assertEqual('bad_request', response.json['type'])
|
||||
|
||||
def test_unset_floatingip(self):
|
||||
self.create_server()
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
# Unsetting via "None"
|
||||
self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
# Unset PTR ('ptrdname' is None aka null in JSON)
|
||||
response = self.client.patch_json(
|
||||
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
|
||||
{'floatingip': {'ptrdname': None}},
|
||||
headers={'X-Test-Tenant-Id': context.tenant})
|
||||
self.assertEqual(None, response.json)
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
fip = self.central_service.get_floatingip(
|
||||
context, fip['region'], fip['id'])
|
||||
self.assertEqual(None, fip['ptrdname'])
|
||||
|
||||
def test_unset_floatingip_not_allocated(self):
|
||||
self.create_server()
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant)
|
||||
|
||||
self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
self.network_api.fake.deallocate_floatingip(fip['id'])
|
||||
|
||||
response = self.client.patch_json(
|
||||
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
|
||||
{"floatingip": {'ptrdname': None}}, status=404)
|
||||
|
||||
self.assertIn('message', response.json)
|
||||
self.assertIn('request_id', response.json)
|
||||
self.assertEqual(404, response.json['code'])
|
||||
self.assertEqual('not_found', response.json['type'])
|
@ -1398,3 +1398,268 @@ class CentralServiceTest(CentralTestCase):
|
||||
|
||||
with testtools.ExpectedException(exceptions.Forbidden):
|
||||
self.central_service.count_records(self.get_context())
|
||||
|
||||
def test_get_floatingip_no_record(self):
|
||||
self.create_server()
|
||||
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
fip_ptr = self.central_service.get_floatingip(
|
||||
context, fip['region'], fip['id'])
|
||||
|
||||
self.assertEqual(fip['region'], fip_ptr['region'])
|
||||
self.assertEqual(fip['id'], fip_ptr['id'])
|
||||
self.assertEqual(fip['address'], fip_ptr['address'])
|
||||
self.assertEqual(None, fip_ptr['ptrdname'])
|
||||
|
||||
def test_get_floatingip_with_record(self):
|
||||
self.create_server()
|
||||
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
expected = self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
actual = self.central_service.get_floatingip(
|
||||
context, fip['region'], fip['id'])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_get_floatingip_not_allocated(self):
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
self.network_api.fake.deallocate_floatingip(fip['id'])
|
||||
|
||||
with testtools.ExpectedException(exceptions.NotFound):
|
||||
self.central_service.get_floatingip(
|
||||
context, fip['region'], fip['id'])
|
||||
|
||||
def test_get_floatingip_deallocated_and_invalidate(self):
|
||||
self.create_server()
|
||||
|
||||
context_a = self.get_context(tenant='a')
|
||||
elevated_a = context_a.elevated()
|
||||
elevated_a.all_tenants = True
|
||||
|
||||
context_b = self.get_context(tenant='b')
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
# First allocate and create a FIP as tenant a
|
||||
fip = self.network_api.fake.allocate_floatingip(context_a.tenant_id)
|
||||
|
||||
self.central_service.update_floatingip(
|
||||
context_a, fip['region'], fip['id'], fixture)
|
||||
|
||||
self.network_api.fake.deallocate_floatingip(fip['id'])
|
||||
|
||||
with testtools.ExpectedException(exceptions.NotFound):
|
||||
self.central_service.get_floatingip(
|
||||
context_a, fip['region'], fip['id'])
|
||||
|
||||
# Ensure that the record is still in DB (No invalidation)
|
||||
criterion = {
|
||||
'managed_resource_id': fip['id'],
|
||||
'managed_tenant_id': context_a.tenant_id}
|
||||
self.central_service.find_record(elevated_a, criterion)
|
||||
|
||||
# Now give the fip id to tenant 'b' and see that it get's deleted
|
||||
self.network_api.fake.allocate_floatingip(
|
||||
context_b.tenant_id, fip['id'])
|
||||
|
||||
# There should be a fip returned with ptrdname of None
|
||||
fip_ptr = self.central_service.get_floatingip(
|
||||
context_b, fip['region'], fip['id'])
|
||||
self.assertEqual(None, fip_ptr['ptrdname'])
|
||||
|
||||
# Ensure that the old record for tenant a for the fip now owned by
|
||||
# tenant b is gone
|
||||
with testtools.ExpectedException(exceptions.RecordNotFound):
|
||||
self.central_service.find_record(elevated_a, criterion)
|
||||
|
||||
def test_list_floatingips_no_allocations(self):
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fips = self.central_service.list_floatingips(context)
|
||||
|
||||
self.assertEqual(0, len(fips))
|
||||
|
||||
def test_list_floatingips_no_record(self):
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
fips = self.central_service.list_floatingips(context)
|
||||
|
||||
self.assertEqual(1, len(fips))
|
||||
self.assertEqual(None, fips[0]['ptrdname'])
|
||||
self.assertEqual(fip['id'], fips[0]['id'])
|
||||
self.assertEqual(fip['region'], fips[0]['region'])
|
||||
self.assertEqual(fip['address'], fips[0]['address'])
|
||||
self.assertEqual(None, fips[0]['description'])
|
||||
|
||||
def test_list_floatingips_with_record(self):
|
||||
self.create_server()
|
||||
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
fip_ptr = self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
fips = self.central_service.list_floatingips(context)
|
||||
|
||||
self.assertEqual(1, len(fips))
|
||||
self.assertEqual(fip_ptr['ptrdname'], fips[0]['ptrdname'])
|
||||
self.assertEqual(fip_ptr['id'], fips[0]['id'])
|
||||
self.assertEqual(fip_ptr['region'], fips[0]['region'])
|
||||
self.assertEqual(fip_ptr['address'], fips[0]['address'])
|
||||
self.assertEqual(fip_ptr['description'], fips[0]['description'])
|
||||
|
||||
def test_list_floatingips_deallocated_and_invalidate(self):
|
||||
self.create_server()
|
||||
|
||||
context_a = self.get_context(tenant='a')
|
||||
elevated_a = context_a.elevated()
|
||||
elevated_a.all_tenants = True
|
||||
|
||||
context_b = self.get_context(tenant='b')
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
# First allocate and create a FIP as tenant a
|
||||
fip = self.network_api.fake.allocate_floatingip(context_a.tenant_id)
|
||||
|
||||
self.central_service.update_floatingip(
|
||||
context_a, fip['region'], fip['id'], fixture)
|
||||
|
||||
self.network_api.fake.deallocate_floatingip(fip['id'])
|
||||
|
||||
fips = self.central_service.list_floatingips(context_a)
|
||||
self.assertEqual([], fips)
|
||||
|
||||
# Ensure that the record is still in DB (No invalidation)
|
||||
criterion = {
|
||||
'managed_resource_id': fip['id'],
|
||||
'managed_tenant_id': context_a.tenant_id}
|
||||
self.central_service.find_record(elevated_a, criterion)
|
||||
|
||||
# Now give the fip id to tenant 'b' and see that it get's deleted
|
||||
self.network_api.fake.allocate_floatingip(
|
||||
context_b.tenant_id, fip['id'])
|
||||
|
||||
# There should be a fip returned with ptrdname of None
|
||||
fips = self.central_service.list_floatingips(context_b)
|
||||
self.assertEqual(1, len(fips))
|
||||
self.assertEqual(None, fips[0]['ptrdname'])
|
||||
|
||||
# Ensure that the old record for tenant a for the fip now owned by
|
||||
# tenant b is gone
|
||||
with testtools.ExpectedException(exceptions.RecordNotFound):
|
||||
self.central_service.find_record(elevated_a, criterion)
|
||||
|
||||
def test_set_floatingip(self):
|
||||
self.create_server()
|
||||
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
fip_ptr = self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
self.assertEqual(fixture['ptrdname'], fip_ptr['ptrdname'])
|
||||
self.assertEqual(fip['address'], fip_ptr['address'])
|
||||
self.assertEqual(None, fip_ptr['description'])
|
||||
self.assertIsNotNone(fip_ptr['ttl'])
|
||||
|
||||
def test_set_floatingip_removes_old_rrset_and_record(self):
|
||||
self.create_server()
|
||||
|
||||
context_a = self.get_context(tenant='a')
|
||||
elevated_a = context_a.elevated()
|
||||
elevated_a.all_tenants = True
|
||||
|
||||
context_b = self.get_context(tenant='b')
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
# Test that re-setting as tenant a an already set floatingip leaves
|
||||
# only 1 record
|
||||
fip = self.network_api.fake.allocate_floatingip(context_a.tenant_id)
|
||||
|
||||
self.central_service.update_floatingip(
|
||||
context_a, fip['region'], fip['id'], fixture)
|
||||
|
||||
fixture2 = self.get_ptr_fixture(fixture=1)
|
||||
self.central_service.update_floatingip(
|
||||
context_a, fip['region'], fip['id'], fixture2)
|
||||
|
||||
count = self.central_service.count_records(
|
||||
elevated_a, {'managed_resource_id': fip['id']})
|
||||
|
||||
self.assertEqual(1, count)
|
||||
|
||||
self.network_api.fake.deallocate_floatingip(fip['id'])
|
||||
|
||||
# Now test that tenant b allocating the same fip and setting a ptr
|
||||
# deletes any records
|
||||
fip = self.network_api.fake.allocate_floatingip(
|
||||
context_b.tenant_id, fip['id'])
|
||||
|
||||
self.central_service.update_floatingip(
|
||||
context_b, fip['region'], fip['id'], fixture)
|
||||
|
||||
count = self.central_service.count_records(
|
||||
elevated_a, {'managed_resource_id': fip['id']})
|
||||
|
||||
self.assertEqual(1, count)
|
||||
|
||||
def test_set_floatingip_not_allocated(self):
|
||||
context = self.get_context(tenant='a')
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
self.network_api.fake.deallocate_floatingip(fip['id'])
|
||||
|
||||
# If one attempts to assign a de-allocated FIP or not-owned it should
|
||||
# fail with BadRequest
|
||||
with testtools.ExpectedException(exceptions.NotFound):
|
||||
fixture = self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
def test_unset_floatingip(self):
|
||||
self.create_server()
|
||||
|
||||
context = self.get_context(tenant='a')
|
||||
|
||||
fixture = self.get_ptr_fixture()
|
||||
|
||||
fip = self.network_api.fake.allocate_floatingip(context.tenant_id)
|
||||
|
||||
fip_ptr = self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], fixture)
|
||||
|
||||
self.assertEqual(fixture['ptrdname'], fip_ptr['ptrdname'])
|
||||
self.assertEqual(fip['address'], fip_ptr['address'])
|
||||
self.assertEqual(None, fip_ptr['description'])
|
||||
self.assertIsNotNone(fip_ptr['ttl'])
|
||||
|
||||
self.central_service.update_floatingip(
|
||||
context, fip['region'], fip['id'], {'ptrdname': None})
|
||||
|
||||
self.central_service.get_floatingip(
|
||||
context, fip['region'], fip['id'])
|
||||
|
0
designate/tests/test_network_api/__init__.py
Normal file
0
designate/tests/test_network_api/__init__.py
Normal file
53
designate/tests/test_network_api/test_neutron.py
Normal file
53
designate/tests/test_network_api/test_neutron.py
Normal file
@ -0,0 +1,53 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@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.
|
||||
from designate import exceptions
|
||||
from designate.network_api import get_api
|
||||
from designate.tests import TestCase
|
||||
|
||||
from neutronclient.v2_0 import client as clientv20
|
||||
from neutronclient.common import exceptions as neutron_exceptions
|
||||
|
||||
from oslo.config import cfg
|
||||
from mock import patch
|
||||
import testtools
|
||||
|
||||
|
||||
cfg.CONF.import_group('network_api:neutron', 'designate.network_api.neutron')
|
||||
|
||||
|
||||
class NeutronAPITest(TestCase):
|
||||
def setUp(self):
|
||||
super(NeutronAPITest, self).setUp()
|
||||
self.config(endpoints=['RegionOne|http://localhost:9696'],
|
||||
group='network_api:neutron')
|
||||
self.api = get_api('neutron')
|
||||
|
||||
@patch.object(clientv20.Client, 'list_floatingips',
|
||||
side_effect=neutron_exceptions.Unauthorized)
|
||||
def test_unauthorized_returns_empty(self, _):
|
||||
context = self.get_context(tenant='a', auth_token='test')
|
||||
|
||||
fips = self.api.list_floatingips(context)
|
||||
self.assertEqual(0, len(fips))
|
||||
|
||||
@patch.object(clientv20.Client, 'list_floatingips',
|
||||
side_effect=neutron_exceptions.NeutronException)
|
||||
def test_communication_failure(self, _):
|
||||
context = self.get_context(tenant='a', auth_token='test')
|
||||
|
||||
with testtools.ExpectedException(
|
||||
exceptions.NeutronCommunicationFailure):
|
||||
self.api.list_floatingips(context)
|
@ -32,6 +32,7 @@ also be found on the `OpenStack wiki`_.
|
||||
production-architecture
|
||||
glossary
|
||||
backends
|
||||
integrations
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
53
doc/source/integrations.rst
Normal file
53
doc/source/integrations.rst
Normal file
@ -0,0 +1,53 @@
|
||||
============
|
||||
Integrations
|
||||
============
|
||||
|
||||
This page overviews integrations with other services like Neutron and others to
|
||||
make use of Designate more convenient.
|
||||
|
||||
Reverse - FloatingIP
|
||||
====================
|
||||
|
||||
The FloatingIP PTR feature of Designate relies on information of the FloatingIP
|
||||
which is in a different service then Designate itself. It can be in any service
|
||||
as long as their is a "plugin" for it that can be loaded via the configuration
|
||||
setting called "network_api".
|
||||
|
||||
* Controller, views and schemas in the V2 API
|
||||
* RPC Client towards Central used by the API and Sink
|
||||
* Logic in Central to make it convenient for setting, unsetting, listing and
|
||||
getting FloatingIP PTR records compared to the Records themselves which would
|
||||
be more work. (This is outlined in code docstrings for the specific methods.)
|
||||
* Sink handlers for the varios backend to help us be more concistent.
|
||||
|
||||
Record invalidation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Happens mainly happens via comparing a Tenant's FloatngIPs
|
||||
towards the list we have of Records which are of a certain plugin type and
|
||||
with the use of a Sink handler that listens for incoming events from the
|
||||
various services.
|
||||
|
||||
Configuring Neutron
|
||||
-------------------
|
||||
|
||||
Configuring the FloatingIP feature is really simple:
|
||||
|
||||
[network_api:neutron]
|
||||
# endpoints = RegionOne|http://localhost:9696
|
||||
# endpoint_type = publicURL
|
||||
# timeout = 30
|
||||
# admin_username = designate
|
||||
# admin_password = designate
|
||||
# admin_tenant_name = designate
|
||||
# auth_url = http://localhost:35357/v2.0
|
||||
# insecure = False
|
||||
# auth_strategy = keystone
|
||||
# ca_certificates_file = /etc/path/to/ca.pem
|
||||
|
||||
Note that using admin_user, admin_password and admin_tenant_name is optional,
|
||||
if not present we'll piggyback on the context.auth_token passed in by the API.
|
||||
|
||||
.. note..
|
||||
If "endpoints" is not configured and there's no service catalog is present
|
||||
in the context passed by the API to Central the request will fail in
|
||||
a NoEndpoint exception.
|
@ -22,6 +22,9 @@ debug = False
|
||||
# Change to "sudo" to skip the filtering and just run the comand directly
|
||||
root_helper = sudo
|
||||
|
||||
# Which networking API to use, Defaults to neutron
|
||||
# network_api = neutron
|
||||
|
||||
########################
|
||||
## Service Configuration
|
||||
########################
|
||||
@ -51,6 +54,15 @@ root_helper = sudo
|
||||
# Maximum record name length
|
||||
#max_record_name_len = 255
|
||||
|
||||
|
||||
## Managed resources settings
|
||||
|
||||
# Email to use for managed resources like domains created by the FloatingIP API
|
||||
# managed_resource_email = root@example.io.
|
||||
|
||||
# Tenant ID to own all managed resources - like auto-created records etc.
|
||||
# managed_resource_tenant_id = 123456
|
||||
|
||||
#-----------------------
|
||||
# API Service
|
||||
#-----------------------
|
||||
@ -99,6 +111,21 @@ root_helper = sudo
|
||||
# correspond to a [handler:my_driver] section below or else in the config
|
||||
#enabled_notification_handlers = nova_fixed
|
||||
|
||||
##############
|
||||
## Network API
|
||||
##############
|
||||
[network_api:neutron]
|
||||
# endpoints = RegionOne|http://localhost:9696
|
||||
# endpoint_type = publicURL
|
||||
# timeout = 30
|
||||
# admin_username = designate
|
||||
# admin_password = designate
|
||||
# admin_tenant_name = designate
|
||||
# auth_url = http://localhost:35357/v2.0
|
||||
# insecure = False
|
||||
# auth_strategy = keystone
|
||||
# ca_certificates_file = /etc/path/to/ca.pem
|
||||
|
||||
########################
|
||||
## Storage Configuration
|
||||
########################
|
||||
|
@ -12,6 +12,7 @@ PasteDeploy>=1.5.0
|
||||
pbr>=0.5.21,<1.0
|
||||
pecan>=0.2.0
|
||||
python-keystoneclient>=0.3.2
|
||||
python-neutronclient>=2.3.0,<3
|
||||
Routes>=1.12.3
|
||||
SQLAlchemy>=0.7.8,<=0.7.99
|
||||
sqlalchemy-migrate>=0.7.2
|
||||
|
@ -69,6 +69,10 @@ designate.backend =
|
||||
nsd4slave = designate.backend.impl_nsd4slave:NSD4SlaveBackend
|
||||
multi = designate.backend.impl_multi:MultiBackend
|
||||
|
||||
designate.network_api =
|
||||
fake = designate.network_api.fake:API
|
||||
neutron = designate.network_api.neutron:API
|
||||
|
||||
designate.quota =
|
||||
noop = designate.quota.impl_noop:NoopQuota
|
||||
storage = designate.quota.impl_storage:StorageQuota
|
||||
|
Loading…
Reference in New Issue
Block a user