Merge "Added functionality to allow for zone ownership transfers"
This commit is contained in:
commit
0abf05b6f6
@ -93,6 +93,14 @@ class RestController(pecan.rest.RestController):
|
||||
|
||||
return marker, limit, sort_key, sort_dir
|
||||
|
||||
def _apply_filter_params(self, params, accepted_filters, criterion):
|
||||
|
||||
for k in accepted_filters:
|
||||
if k in params:
|
||||
criterion[k] = params[k]
|
||||
|
||||
return criterion
|
||||
|
||||
def _handle_post(self, method, remainder):
|
||||
'''
|
||||
Routes ``POST`` actions to the appropriate controller.
|
||||
|
@ -21,10 +21,10 @@ 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 tlds
|
||||
from designate.api.v2.controllers import zones
|
||||
from designate.api.v2.controllers import blacklists
|
||||
from designate.api.v2.controllers import errors
|
||||
from designate.api.v2.controllers import pools
|
||||
from designate.api.v2.controllers import zones
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -24,6 +24,7 @@ from designate import dnsutils
|
||||
from designate.api.v2.controllers import rest
|
||||
from designate.api.v2.controllers import nameservers
|
||||
from designate.api.v2.controllers import recordsets
|
||||
from designate.api.v2.controllers.zones import tasks
|
||||
from designate.api.v2.views import zones as zones_view
|
||||
from designate.objects import Domain
|
||||
|
||||
@ -37,6 +38,7 @@ class ZonesController(rest.RestController):
|
||||
|
||||
nameservers = nameservers.NameServersController()
|
||||
recordsets = recordsets.RecordSetsController()
|
||||
tasks = tasks.TasksController()
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
@pecan.expose(template=None, content_type='text/dns')
|
29
designate/api/v2/controllers/zones/tasks/__init__.py
Normal file
29
designate/api/v2/controllers/zones/tasks/__init__.py
Normal file
@ -0,0 +1,29 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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.zones.tasks.transfer_requests \
|
||||
import TransferRequestsController as TRC
|
||||
from designate.api.v2.controllers.zones.tasks.transfer_accepts \
|
||||
import TransferAcceptsController as TRA
|
||||
from designate.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TasksController(rest.RestController):
|
||||
|
||||
transfer_accepts = TRA()
|
||||
transfer_requests = TRC()
|
72
designate/api/v2/controllers/zones/tasks/transfer_accepts.py
Normal file
72
designate/api/v2/controllers/zones/tasks/transfer_accepts.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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
|
||||
|
||||
from designate.openstack.common import log as logging
|
||||
from designate import schema
|
||||
from designate import utils
|
||||
from designate.api.v2.controllers import rest
|
||||
from designate.api.v2.views.zones.tasks import transfer_accepts as \
|
||||
zone_transfer_accepts_view
|
||||
from designate.objects import ZoneTransferAccept
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TransferAcceptsController(rest.RestController):
|
||||
_view = zone_transfer_accepts_view.ZoneTransferAcceptsView()
|
||||
_resource_schema = schema.Schema('v2', 'transfer_accept')
|
||||
_collection_schema = schema.Schema('v2', 'transfer_accepts')
|
||||
SORT_KEYS = ['created_at', 'id', 'updated_at']
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
@utils.validate_uuid('transfer_accept_id')
|
||||
def get_one(self, transfer_accept_id):
|
||||
"""Get transfer_accepts"""
|
||||
|
||||
request = pecan.request
|
||||
context = request.environ['context']
|
||||
|
||||
transfer_accepts = \
|
||||
self.central_api.get_zone_transfer_accept(
|
||||
context, transfer_accept_id)
|
||||
|
||||
return self._view.show(context, request, transfer_accepts)
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
def post_all(self):
|
||||
"""Create ZoneTransferAccept"""
|
||||
request = pecan.request
|
||||
response = pecan.response
|
||||
context = request.environ['context']
|
||||
body = request.body_dict
|
||||
|
||||
# Validate the request conforms to the schema
|
||||
self._resource_schema.validate(body)
|
||||
|
||||
# Convert from APIv2 -> Central format
|
||||
values = self._view.load(context, request, body)
|
||||
# Create the zone_transfer_request
|
||||
zone_transfer_accept = self.central_api.create_zone_transfer_accept(
|
||||
context, ZoneTransferAccept(**values))
|
||||
response.status_int = 201
|
||||
|
||||
response.headers['Location'] = self._view._get_resource_href(
|
||||
request,
|
||||
zone_transfer_accept)
|
||||
# Prepare and return the response body
|
||||
return self._view.show(context, request, zone_transfer_accept)
|
144
designate/api/v2/controllers/zones/tasks/transfer_requests.py
Normal file
144
designate/api/v2/controllers/zones/tasks/transfer_requests.py
Normal file
@ -0,0 +1,144 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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
|
||||
|
||||
from designate.openstack.common import log as logging
|
||||
from designate import schema
|
||||
from designate import utils
|
||||
from designate.api.v2.controllers import rest
|
||||
from designate.api.v2.views.zones.tasks import transfer_requests as \
|
||||
zone_transfer_requests_view
|
||||
from designate.objects import ZoneTransferRequest
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TransferRequestsController(rest.RestController):
|
||||
_view = zone_transfer_requests_view.ZoneTransferRequestsView()
|
||||
_resource_schema = schema.Schema('v2', 'transfer_request')
|
||||
_collection_schema = schema.Schema('v2', 'transfer_requests')
|
||||
SORT_KEYS = ['created_at', 'id', 'updated_at']
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
@utils.validate_uuid('transfer_request_id')
|
||||
def get_one(self, transfer_request_id):
|
||||
"""Get transfer_request"""
|
||||
|
||||
request = pecan.request
|
||||
context = request.environ['context']
|
||||
|
||||
transfer_request = \
|
||||
self.central_api.get_zone_transfer_request(
|
||||
context, transfer_request_id)
|
||||
|
||||
return self._view.show(context, request, transfer_request)
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
def get_all(self, **params):
|
||||
"""List ZoneTransferRequests"""
|
||||
request = pecan.request
|
||||
context = request.environ['context']
|
||||
|
||||
# Extract the pagination params
|
||||
marker, limit, sort_key, sort_dir = self._get_paging_params(params)
|
||||
|
||||
# Extract any filter params.
|
||||
criterion = self._apply_filter_params(params, ('status',), {})
|
||||
|
||||
zone_transfer_requests = self.central_api.find_zone_transfer_requests(
|
||||
context, criterion, marker, limit, sort_key, sort_dir)
|
||||
|
||||
return self._view.list(context, request, zone_transfer_requests)
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
@utils.validate_uuid('zone_id')
|
||||
def post_all(self, zone_id):
|
||||
"""Create ZoneTransferRequest"""
|
||||
request = pecan.request
|
||||
response = pecan.response
|
||||
context = request.environ['context']
|
||||
body = request.body_dict
|
||||
|
||||
if body['transfer_request'] is not None:
|
||||
body['transfer_request']['zone_id'] = zone_id
|
||||
|
||||
# Validate the request conforms to the schema
|
||||
self._resource_schema.validate(body)
|
||||
|
||||
# Convert from APIv2 -> Central format
|
||||
values = self._view.load(context, request, body)
|
||||
|
||||
# Create the zone_transfer_request
|
||||
zone_transfer_request = self.central_api.create_zone_transfer_request(
|
||||
context, ZoneTransferRequest(**values))
|
||||
response.status_int = 201
|
||||
|
||||
response.headers['Location'] = self._view._get_resource_href(
|
||||
request,
|
||||
zone_transfer_request)
|
||||
# Prepare and return the response body
|
||||
return self._view.show(context, request, zone_transfer_request)
|
||||
|
||||
@pecan.expose(template='json:', content_type='application/json')
|
||||
@pecan.expose(template='json:', content_type='application/json-patch+json')
|
||||
@utils.validate_uuid('zone_transfer_request_id')
|
||||
def patch_one(self, zone_transfer_request_id):
|
||||
"""Update ZoneTransferRequest"""
|
||||
request = pecan.request
|
||||
context = request.environ['context']
|
||||
body = request.body_dict
|
||||
response = pecan.response
|
||||
|
||||
# Fetch the existing zone_transfer_request
|
||||
zt_request = self.central_api.get_zone_transfer_request(
|
||||
context, zone_transfer_request_id)
|
||||
|
||||
# Convert to APIv2 Format
|
||||
zt_request_data = self._view.show(context,
|
||||
request, zt_request)
|
||||
|
||||
if request.content_type == 'application/json-patch+json':
|
||||
raise NotImplemented('json-patch not implemented')
|
||||
else:
|
||||
zt_request_data = utils.deep_dict_merge(
|
||||
zt_request_data, body)
|
||||
|
||||
# Validate the request conforms to the schema
|
||||
self._resource_schema.validate(zt_request_data)
|
||||
|
||||
zt_request.update(self._view.load(context, request, body))
|
||||
zt_request = self.central_api.update_zone_transfer_request(
|
||||
context, zt_request)
|
||||
|
||||
response.status_int = 200
|
||||
|
||||
return self._view.show(context, request, zt_request)
|
||||
|
||||
@pecan.expose(template=None, content_type='application/json')
|
||||
@utils.validate_uuid('zone_transfer_request_id')
|
||||
def delete_one(self, zone_transfer_request_id):
|
||||
"""Delete ZoneTransferRequest"""
|
||||
request = pecan.request
|
||||
response = pecan.response
|
||||
context = request.environ['context']
|
||||
|
||||
self.central_api.delete_zone_transfer_request(
|
||||
context, zone_transfer_request_id)
|
||||
|
||||
response.status_int = 204
|
||||
|
||||
# NOTE: This is a hack and a half.. But Pecan needs it.
|
||||
return ''
|
0
designate/api/v2/views/zones/tasks/__init__.py
Normal file
0
designate/api/v2/views/zones/tasks/__init__.py
Normal file
52
designate/api/v2/views/zones/tasks/transfer_accepts.py
Normal file
52
designate/api/v2/views/zones/tasks/transfer_accepts.py
Normal file
@ -0,0 +1,52 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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 ZoneTransferAcceptsView(base_view.BaseView):
|
||||
"""Model a ZoneTransferRequest API response as a python dictionary"""
|
||||
|
||||
_resource_name = 'transfer_accept'
|
||||
_collection_name = 'transfer_accepts'
|
||||
|
||||
def _get_base_href(self, parents=None):
|
||||
href = "%s/v2/zones/tasks/%s" % (self.base_uri, self._collection_name)
|
||||
return href.rstrip('?')
|
||||
|
||||
def _get_resource_links(self, request, item):
|
||||
return {
|
||||
"self": self._get_resource_href(request, item),
|
||||
"zone": "%s/v2/zones/%s" % (self.base_uri, item.domain_id)
|
||||
}
|
||||
|
||||
def show_basic(self, context, request, zt_accept):
|
||||
"""Basic view of a ZoneTransferRequest"""
|
||||
|
||||
return {
|
||||
"id": zt_accept.id,
|
||||
"status": zt_accept.status,
|
||||
"links": self._get_resource_links(request, zt_accept)
|
||||
}
|
||||
|
||||
def load(self, context, request, body):
|
||||
"""Extract a "central" compatible dict from an API call"""
|
||||
valid_keys = ('zone_transfer_request_id', 'key')
|
||||
|
||||
return self._load(context, request, body, valid_keys)
|
84
designate/api/v2/views/zones/tasks/transfer_requests.py
Normal file
84
designate/api/v2/views/zones/tasks/transfer_requests.py
Normal file
@ -0,0 +1,84 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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
|
||||
from designate import exceptions
|
||||
from designate import policy
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ZoneTransferRequestsView(base_view.BaseView):
|
||||
"""Model a ZoneTransferRequest API response as a python dictionary"""
|
||||
|
||||
_resource_name = 'transfer_request'
|
||||
_collection_name = 'transfer_requests'
|
||||
|
||||
def _get_base_href(self, parents=None):
|
||||
href = "%s/v2/zones/tasks/%s" % (self.base_uri, self._collection_name)
|
||||
return href.rstrip('?')
|
||||
|
||||
def show_basic(self, context, request, zt_request):
|
||||
"""Basic view of a ZoneTransferRequest"""
|
||||
|
||||
try:
|
||||
target = {
|
||||
'tenant_id': zt_request.tenant_id,
|
||||
}
|
||||
|
||||
policy.check('get_zone_transfer_request_detailed', context, target)
|
||||
|
||||
except exceptions.Forbidden:
|
||||
return {
|
||||
"id": zt_request.id,
|
||||
"description": zt_request.description,
|
||||
"zone_id": zt_request.domain_id,
|
||||
"zone_name": zt_request.domain_name,
|
||||
"status": zt_request.status,
|
||||
"links": self._get_resource_links(request, zt_request)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"id": zt_request.id,
|
||||
"description": zt_request.description,
|
||||
"zone_id": zt_request.domain_id,
|
||||
"zone_name": zt_request.domain_name,
|
||||
"target_project_id": zt_request.target_tenant_id,
|
||||
"project_id": zt_request.tenant_id,
|
||||
"created_at": zt_request.created_at,
|
||||
"updated_at": zt_request.updated_at,
|
||||
"status": zt_request.status,
|
||||
"key": zt_request.key,
|
||||
"links": self._get_resource_links(request, zt_request)
|
||||
}
|
||||
|
||||
def load(self, context, request, body):
|
||||
"""Extract a "central" compatible dict from an API call"""
|
||||
valid_keys = ('description', 'domain_id', 'target_tenant_id')
|
||||
|
||||
zt_request = body["transfer_request"]
|
||||
old_keys = {
|
||||
'zone_id': 'domain_id',
|
||||
'project_id': 'tenant_id',
|
||||
'target_project_id': 'target_tenant_id',
|
||||
}
|
||||
for key in zt_request:
|
||||
if key in old_keys:
|
||||
zt_request[old_keys[key]] = ''
|
||||
zt_request[old_keys[key]] = zt_request.pop(key)
|
||||
|
||||
return self._load(context, request, body, valid_keys)
|
@ -43,14 +43,16 @@ class CentralAPI(object):
|
||||
4.0 - Create methods now accept designate objects
|
||||
4.1 - Add methods for server pools
|
||||
4.2 - Add methods for pool manager integration
|
||||
4.3 - Added Zone Transfer Methods
|
||||
|
||||
"""
|
||||
RPC_API_VERSION = '4.2'
|
||||
RPC_API_VERSION = '4.3'
|
||||
|
||||
def __init__(self, topic=None):
|
||||
topic = topic if topic else cfg.CONF.central_topic
|
||||
|
||||
target = messaging.Target(topic=topic, version=self.RPC_API_VERSION)
|
||||
self.client = rpc.get_client(target, version_cap='4.2')
|
||||
self.client = rpc.get_client(target, version_cap='4.3')
|
||||
|
||||
# Misc Methods
|
||||
def get_absolute_limits(self, context):
|
||||
@ -413,3 +415,108 @@ class CentralAPI(object):
|
||||
cctxt = self.client.prepare(version='4.2')
|
||||
return cctxt.call(context, 'update_status', domain_id=domain_id,
|
||||
status=status, serial=serial)
|
||||
|
||||
# Zone Ownership Transfers
|
||||
def create_zone_transfer_request(self, context, zone_transfer_request):
|
||||
LOG.info(_LI("create_zone_transfer_request: \
|
||||
Calling central's create_zone_transfer_request."))
|
||||
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context, 'create_zone_transfer_request',
|
||||
zone_transfer_request=zone_transfer_request)
|
||||
|
||||
def get_zone_transfer_request(self, context, zone_transfer_request_id):
|
||||
LOG.info(_LI("get_zone_transfer_request: \
|
||||
Calling central's get_zone_transfer_request."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context,
|
||||
'get_zone_transfer_request',
|
||||
zone_transfer_request_id=zone_transfer_request_id)
|
||||
|
||||
def find_zone_transfer_requests(self, context, criterion=None, marker=None,
|
||||
limit=None, sort_key=None, sort_dir=None):
|
||||
LOG.info(_LI("find_zone_transfer_requests: \
|
||||
Calling central's find_zone_transfer_requests."))
|
||||
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context, 'find_zone_transfer_requests', criterion=criterion,
|
||||
marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
def find_zone_transfer_request(self, context, zone_transfer_request):
|
||||
LOG.info(_LI("find_zone_transfer_request: \
|
||||
Calling central's find_zone_transfer_request."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context, 'find_zone_transfer_request',
|
||||
zone_transfer_request=zone_transfer_request)
|
||||
|
||||
def update_zone_transfer_request(self, context, zone_transfer_request):
|
||||
LOG.info(_LI("update_zone_transfer_request: \
|
||||
Calling central's update_zone_transfer_request."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context, 'update_zone_transfer_request',
|
||||
zone_transfer_request=zone_transfer_request)
|
||||
|
||||
def delete_zone_transfer_request(self, context, zone_transfer_request_id):
|
||||
LOG.info(_LI("delete_zone_transfer_request: \
|
||||
Calling central's delete_zone_transfer_request."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context,
|
||||
'delete_zone_transfer_request',
|
||||
zone_transfer_request_id=zone_transfer_request_id)
|
||||
|
||||
def create_zone_transfer_accept(self, context, zone_transfer_accept):
|
||||
LOG.info(_LI("create_zone_transfer_accept: \
|
||||
Calling central's create_zone_transfer_accept."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context, 'create_zone_transfer_accept',
|
||||
zone_transfer_accept=zone_transfer_accept)
|
||||
|
||||
def get_zone_transfer_accept(self, context, zone_transfer_accept_id):
|
||||
LOG.info(_LI("get_zone_transfer_accept: \
|
||||
Calling central's get_zone_transfer_accept."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context,
|
||||
'get_zone_transfer_accept',
|
||||
zone_transfer_accept_id=zone_transfer_accept_id)
|
||||
|
||||
def find_zone_transfer_accepts(self, context, criterion=None, marker=None,
|
||||
limit=None, sort_key=None, sort_dir=None):
|
||||
LOG.info(_LI("find_zone_transfer_accepts: \
|
||||
Calling central's find_zone_transfer_accepts."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context, 'find_zone_transfer_accepts', criterion=criterion,
|
||||
marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
def find_zone_transfer_accept(self, context, zone_transfer_accept):
|
||||
LOG.info(_LI("find_zone_transfer_accept: \
|
||||
Calling central's find_zone_transfer_accept."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context, 'find_zone_transfer_accept',
|
||||
zone_transfer_accept=zone_transfer_accept)
|
||||
|
||||
def update_zone_transfer_accept(self, context, zone_transfer_accept):
|
||||
LOG.info(_LI("update_zone_transfer_accept: \
|
||||
Calling central's update_zone_transfer_accept."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context, 'update_zone_transfer_accept',
|
||||
zone_transfer_accept=zone_transfer_accept)
|
||||
|
||||
def delete_zone_transfer_accept(self, context, zone_transfer_accept_id):
|
||||
LOG.info(_LI("delete_zone_transfer_accept: \
|
||||
Calling central's delete_zone_transfer_accept."))
|
||||
cctxt = self.client.prepare(version='4.3')
|
||||
return cctxt.call(
|
||||
context,
|
||||
'delete_zone_transfer_accept',
|
||||
zone_transfer_accept_id=zone_transfer_accept_id)
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2013 - 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
@ -20,6 +20,8 @@ import collections
|
||||
import functools
|
||||
import threading
|
||||
import itertools
|
||||
import string
|
||||
import random
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo import messaging
|
||||
@ -113,7 +115,9 @@ def synchronized_domain(domain_arg=1, new_domain=False):
|
||||
break
|
||||
|
||||
elif (isinstance(arg, objects.RecordSet) or
|
||||
isinstance(arg, objects.Record)):
|
||||
isinstance(arg, objects.Record) or
|
||||
isinstance(arg, objects.ZoneTransferRequest) or
|
||||
isinstance(arg, objects.ZoneTransferAccept)):
|
||||
|
||||
domain_id = arg.domain_id
|
||||
if domain_id is not None:
|
||||
@ -196,7 +200,7 @@ def notification(notification_type):
|
||||
|
||||
|
||||
class Service(service.RPCService):
|
||||
RPC_API_VERSION = '4.2'
|
||||
RPC_API_VERSION = '4.3'
|
||||
|
||||
target = messaging.Target(version=RPC_API_VERSION)
|
||||
|
||||
@ -1716,10 +1720,11 @@ class Service(service.RPCService):
|
||||
zone = self.storage.find_domain(
|
||||
elevated_context, {'name': zone_name})
|
||||
except exceptions.DomainNotFound:
|
||||
msg = _LI('Creating zone for %(fip_id)s:%(region)s - '
|
||||
'%(fip_addr)s zone %(zonename)s') % \
|
||||
{'fip_id': floatingip_id, 'region': region,
|
||||
'fip_addr': fip['address'], 'zonename': zone_name}
|
||||
msg = _LI(
|
||||
'Creating zone for %(fip_id)s:%(region)s - '
|
||||
'%(fip_addr)s zone %(zonename)s') % \
|
||||
{'fip_id': floatingip_id, 'region': region,
|
||||
'fip_addr': fip['address'], 'zonename': zone_name}
|
||||
LOG.info(msg)
|
||||
|
||||
email = cfg.CONF['service:central'].managed_resource_email
|
||||
@ -1737,7 +1742,7 @@ class Service(service.RPCService):
|
||||
record_name = self.network_api.address_name(fip['address'])
|
||||
|
||||
try:
|
||||
# NOTE: Delete the current recormdset if any (also purges records)
|
||||
# NOTE: Delete the current recordset if any (also purges records)
|
||||
LOG.debug("Removing old RRset / Record")
|
||||
rset = self.find_recordset(
|
||||
elevated_context, {'name': record_name, 'type': 'PTR'})
|
||||
@ -1987,3 +1992,207 @@ class Service(service.RPCService):
|
||||
self.storage.update_record(context, record)
|
||||
|
||||
self.storage.update_domain(context, domain)
|
||||
|
||||
# Zone Transfers
|
||||
def _transfer_key_generator(self, size=8):
|
||||
chars = string.ascii_uppercase + string.digits
|
||||
return ''.join(random.choice(chars) for _ in range(size))
|
||||
|
||||
@notification('dns.zone_transfer_request.create')
|
||||
@transaction
|
||||
def create_zone_transfer_request(self, context, zone_transfer_request):
|
||||
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
# get zone
|
||||
zone = self.get_domain(context, zone_transfer_request.domain_id)
|
||||
target = {
|
||||
'tenant_id': zone.tenant_id,
|
||||
}
|
||||
policy.check('create_zone_transfer_request', context, target)
|
||||
|
||||
zone_transfer_request.key = self._transfer_key_generator()
|
||||
|
||||
if zone_transfer_request.tenant_id is None:
|
||||
zone_transfer_request.tenant_id = context.tenant
|
||||
|
||||
created_zone_transfer_request = \
|
||||
self.storage.create_zone_transfer_request(
|
||||
context, zone_transfer_request)
|
||||
|
||||
return created_zone_transfer_request
|
||||
|
||||
def get_zone_transfer_request(self, context, zone_transfer_request_id):
|
||||
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
|
||||
# Get zone transfer request
|
||||
zone_transfer_request = self.storage.get_zone_transfer_request(
|
||||
elevated_context, zone_transfer_request_id)
|
||||
|
||||
LOG.info(_LI('Target Tenant ID found - using scoped policy'))
|
||||
target = {
|
||||
'target_tenant_id': zone_transfer_request.target_tenant_id,
|
||||
'tenant_id': zone_transfer_request.tenant_id,
|
||||
}
|
||||
policy.check('get_zone_transfer_request', context, target)
|
||||
|
||||
return zone_transfer_request
|
||||
|
||||
def find_zone_transfer_requests(self, context, criterion=None, marker=None,
|
||||
limit=None, sort_key=None, sort_dir=None):
|
||||
|
||||
policy.check('find_zone_transfer_requests', context)
|
||||
|
||||
requests = self.storage.find_zone_transfer_requests(
|
||||
context, criterion,
|
||||
marker, limit,
|
||||
sort_key, sort_dir)
|
||||
|
||||
return requests
|
||||
|
||||
def find_zone_transfer_request(self, context, criterion):
|
||||
target = {
|
||||
'tenant_id': context.tenant,
|
||||
}
|
||||
policy.check('find_zone_transfer_request', context, target)
|
||||
return self.storage.find_zone_transfer_requests(context, criterion)
|
||||
|
||||
@notification('dns.zone_transfer_request.update')
|
||||
@transaction
|
||||
def update_zone_transfer_request(self, context, zone_transfer_request):
|
||||
|
||||
if 'domain_id' in zone_transfer_request.obj_what_changed():
|
||||
raise exceptions.InvalidOperation('Domain cannot be changed')
|
||||
|
||||
target = {
|
||||
'tenant_id': zone_transfer_request.tenant_id,
|
||||
}
|
||||
policy.check('update_zone_transfer_request', context, target)
|
||||
request = self.storage.update_zone_transfer_request(
|
||||
context, zone_transfer_request)
|
||||
|
||||
return request
|
||||
|
||||
@notification('dns.zone_transfer_request.delete')
|
||||
@transaction
|
||||
def delete_zone_transfer_request(self, context, zone_transfer_request_id):
|
||||
# Get zone transfer request
|
||||
zone_transfer_request = self.storage.get_zone_transfer_request(
|
||||
context, zone_transfer_request_id)
|
||||
target = {
|
||||
'tenant_id': zone_transfer_request.tenant_id,
|
||||
}
|
||||
policy.check('delete_zone_transfer_request', context, target)
|
||||
return self.storage.delete_zone_transfer_request(
|
||||
context,
|
||||
zone_transfer_request_id)
|
||||
|
||||
@notification('dns.zone_transfer_accept.create')
|
||||
@transaction
|
||||
def create_zone_transfer_accept(self, context, zone_transfer_accept):
|
||||
elevated_context = context.elevated()
|
||||
elevated_context.all_tenants = True
|
||||
zone_transfer_request = self.get_zone_transfer_request(
|
||||
context, zone_transfer_accept.zone_transfer_request_id)
|
||||
|
||||
zone_transfer_accept.domain_id = zone_transfer_request.domain_id
|
||||
|
||||
if zone_transfer_request.status != 'ACTIVE':
|
||||
if zone_transfer_request.status == 'COMPLETE':
|
||||
raise exceptions.InvaildZoneTransfer(
|
||||
'Zone Transfer Request has been used')
|
||||
raise exceptions.InvaildZoneTransfer(
|
||||
'Zone Transfer Request Invalid')
|
||||
|
||||
if zone_transfer_request.key != zone_transfer_accept.key:
|
||||
raise exceptions.IncorrectZoneTransferKey(
|
||||
'Key does not match stored key for request')
|
||||
|
||||
target = {
|
||||
'target_tenant_id': zone_transfer_request.target_tenant_id
|
||||
}
|
||||
policy.check('create_zone_transfer_accept', context, target)
|
||||
|
||||
if zone_transfer_accept.tenant_id is None:
|
||||
zone_transfer_accept.tenant_id = context.tenant
|
||||
|
||||
created_zone_transfer_accept = \
|
||||
self.storage.create_zone_transfer_accept(
|
||||
context, zone_transfer_accept)
|
||||
|
||||
try:
|
||||
domain = self.storage.get_domain(
|
||||
elevated_context,
|
||||
zone_transfer_request.domain_id)
|
||||
|
||||
domain.tenant_id = zone_transfer_accept.tenant_id
|
||||
self.storage.update_domain(elevated_context, domain)
|
||||
|
||||
except Exception:
|
||||
created_zone_transfer_accept.status = 'ERROR'
|
||||
self.storage.update_zone_transfer_accept(
|
||||
context, created_zone_transfer_accept)
|
||||
raise
|
||||
else:
|
||||
created_zone_transfer_accept.status = 'COMPLETE'
|
||||
zone_transfer_request.status = 'COMPLETE'
|
||||
self.storage.update_zone_transfer_accept(
|
||||
context, created_zone_transfer_accept)
|
||||
self.storage.update_zone_transfer_request(
|
||||
elevated_context, zone_transfer_request)
|
||||
|
||||
return created_zone_transfer_accept
|
||||
|
||||
def get_zone_transfer_accept(self, context, zone_transfer_accept_id):
|
||||
# Get zone transfer accept
|
||||
|
||||
zone_transfer_accept = self.storage.get_zone_transfer_accept(
|
||||
context, zone_transfer_accept_id)
|
||||
|
||||
target = {
|
||||
'tenant_id': zone_transfer_accept.tenant_id
|
||||
}
|
||||
policy.check('get_zone_transfer_accept', context, target)
|
||||
|
||||
return zone_transfer_accept
|
||||
|
||||
def find_zone_transfer_accepts(self, context, criterion=None, marker=None,
|
||||
limit=None, sort_key=None, sort_dir=None):
|
||||
policy.check('find_zone_transfer_accepts', context)
|
||||
return self.storage.find_zone_transfer_accepts(context, criterion,
|
||||
marker, limit,
|
||||
sort_key, sort_dir)
|
||||
|
||||
def find_zone_transfer_accept(self, context, criterion):
|
||||
policy.check('find_zone_transfer_accept', context)
|
||||
return self.storage.find_zone_transfer_accept(context, criterion)
|
||||
|
||||
@notification('dns.zone_transfer_accept.update')
|
||||
@transaction
|
||||
def update_zone_transfer_accept(self, context, zone_transfer_accept):
|
||||
target = {
|
||||
'tenant_id': zone_transfer_accept.tenant_id
|
||||
}
|
||||
policy.check('update_zone_transfer_accept', context, target)
|
||||
accept = self.storage.update_zone_transfer_accept(
|
||||
context, zone_transfer_accept)
|
||||
|
||||
return accept
|
||||
|
||||
@notification('dns.zone_transfer_accept.delete')
|
||||
@transaction
|
||||
def delete_zone_transfer_accept(self, context, zone_transfer_accept_id):
|
||||
# Get zone transfer accept
|
||||
zt_accept = self.storage.get_zone_transfer_accept(
|
||||
context, zone_transfer_accept_id)
|
||||
|
||||
target = {
|
||||
'tenant_id': zt_accept.tenant_id
|
||||
}
|
||||
policy.check('delete_zone_transfer_accept', context, target)
|
||||
return self.storage.delete_zone_transfer_accept(
|
||||
context,
|
||||
zone_transfer_accept_id)
|
||||
|
@ -159,6 +159,11 @@ class InvalidRecordSetLocation(Base):
|
||||
error_type = 'invalid_recordset_location'
|
||||
|
||||
|
||||
class InvaildZoneTransfer(Base):
|
||||
error_code = 400
|
||||
error_type = 'invalid_zone_transfer_request'
|
||||
|
||||
|
||||
class InvalidTTL(Base):
|
||||
error_code = 400
|
||||
error_type = 'invalid_ttl'
|
||||
@ -175,6 +180,10 @@ class Forbidden(Base):
|
||||
expected = True
|
||||
|
||||
|
||||
class IncorrectZoneTransferKey(Forbidden):
|
||||
error_type = 'invalid_key'
|
||||
|
||||
|
||||
class Duplicate(Base):
|
||||
expected = True
|
||||
error_code = 409
|
||||
@ -231,6 +240,14 @@ class MethodNotAllowed(Base):
|
||||
error_type = 'method_not_allowed'
|
||||
|
||||
|
||||
class DuplicateZoneTransferRequest(Duplicate):
|
||||
error_type = 'duplicate_zone_transfer_request'
|
||||
|
||||
|
||||
class DuplicateZoneTransferAccept(Duplicate):
|
||||
error_type = 'duplicate_zone_transfer_accept'
|
||||
|
||||
|
||||
class NotFound(Base):
|
||||
expected = True
|
||||
error_code = 404
|
||||
@ -285,6 +302,14 @@ class PoolAttributeNotFound(NotFound):
|
||||
error_type = 'pool_attribute_not_found'
|
||||
|
||||
|
||||
class ZoneTransferRequestNotFound(NotFound):
|
||||
error_type = 'zone_transfer_request_not_found'
|
||||
|
||||
|
||||
class ZoneTransferAcceptNotFound(NotFound):
|
||||
error_type = 'zone_transfer_accept_not_found'
|
||||
|
||||
|
||||
class LastServerDeleteNotAllowed(BadRequest):
|
||||
error_type = 'last_server_delete_not_allowed'
|
||||
|
||||
|
@ -46,3 +46,5 @@ from designate.objects.tld import Tld, TldList # noqa
|
||||
from designate.objects.tsigkey import TsigKey, TsigKeyList # noqa
|
||||
from designate.objects.validation_error import ValidationError # noqa
|
||||
from designate.objects.validation_error import ValidationErrorList # noqa
|
||||
from designate.objects.zone_transfer_request import ZoneTransferRequest, ZoneTransferRequestList # noqa
|
||||
from designate.objects.zone_transfer_accept import ZoneTransferAccept, ZoneTransferAcceptList # noqa
|
||||
|
31
designate/objects/zone_transfer_accept.py
Normal file
31
designate/objects/zone_transfer_accept.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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.objects import base
|
||||
|
||||
|
||||
class ZoneTransferAccept(base.DictObjectMixin, base.PersistentObjectMixin,
|
||||
base.DesignateObject):
|
||||
FIELDS = {
|
||||
'zone_transfer_request_id': {},
|
||||
'tenant_id': {},
|
||||
'status': {},
|
||||
'key': {},
|
||||
'domain_id': {},
|
||||
}
|
||||
|
||||
|
||||
class ZoneTransferAcceptList(base.ListObjectMixin, base.DesignateObject):
|
||||
LIST_ITEM_TYPE = ZoneTransferAccept
|
37
designate/objects/zone_transfer_request.py
Normal file
37
designate/objects/zone_transfer_request.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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.objects import base
|
||||
|
||||
|
||||
class ZoneTransferRequest(base.DictObjectMixin, base.DesignateObject,
|
||||
base.PersistentObjectMixin):
|
||||
FIELDS = {
|
||||
'domain_id': {},
|
||||
'key': {},
|
||||
'description': {},
|
||||
'tenant_id': {},
|
||||
'target_tenant_id': {},
|
||||
'status': {},
|
||||
'id': {},
|
||||
'created_at': {},
|
||||
'domain_name': {},
|
||||
'updated_at': {},
|
||||
'version': {},
|
||||
}
|
||||
|
||||
|
||||
class ZoneTransferRequestList(base.ListObjectMixin, base.DesignateObject):
|
||||
LIST_ITEM_TYPE = ZoneTransferRequest
|
@ -81,7 +81,6 @@ def init(default_rule=None):
|
||||
def check(rule, ctxt, target=None, do_raise=True, exc=exceptions.Forbidden):
|
||||
creds = ctxt.to_dict()
|
||||
target = target or {}
|
||||
|
||||
try:
|
||||
result = _ENFORCER.enforce(rule, target, creds, do_raise, exc)
|
||||
except Exception:
|
||||
|
67
designate/resources/schemas/v2/transfer_accept.json
Normal file
67
designate/resources/schemas/v2/transfer_accept.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/hyper-schema",
|
||||
|
||||
"id": "zone_transfer_accept",
|
||||
|
||||
"title": "zone_transfer_accept",
|
||||
"description": "Zone Transfer Accept",
|
||||
"additionalProperties": false,
|
||||
|
||||
"required": ["transfer_accept"],
|
||||
|
||||
"properties": {
|
||||
"transfer_accept": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["zone_transfer_request_id", "key"],
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Zone Transfer Request identifier",
|
||||
"pattern": "^([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
|
||||
},
|
||||
"zone_transfer_request_id": {
|
||||
"type": "string",
|
||||
"description": "Request identifier",
|
||||
"pattern": "^([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}$"
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Password used to complete the transfer",
|
||||
"maxLength": 160
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "Zone Status",
|
||||
"enum": ["ACTIVE", "PENDING", "DELETED", "ERROR", "COMPLETE"],
|
||||
"readOnly": true
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"description": "Date and time of Request creation",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
},
|
||||
"updated_at": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Date and time of last Request modification",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"self": {
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
designate/resources/schemas/v2/transfer_accepts.json
Normal file
38
designate/resources/schemas/v2/transfer_accepts.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/hyper-schema",
|
||||
|
||||
"id": "zone_transfer_accept",
|
||||
|
||||
"title": "zone_transfer_accept",
|
||||
"description": "Zone Transfer Accept",
|
||||
"additionalProperties": false,
|
||||
|
||||
"required": ["transfer_accepts"],
|
||||
|
||||
"properties": {
|
||||
"transfer_accepts": {
|
||||
"type": "array",
|
||||
"description": "Zone Transfer Requests",
|
||||
"items": {"$ref": "transfer_accept#/properties/transfer_accept"}
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"self": {
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"next": {
|
||||
"type": ["string", "null"],
|
||||
"format": "url"
|
||||
},
|
||||
"previous": {
|
||||
"type": ["string", "null"],
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
designate/resources/schemas/v2/transfer_request.json
Normal file
90
designate/resources/schemas/v2/transfer_request.json
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/hyper-schema",
|
||||
|
||||
"id": "zone_transfer_request",
|
||||
|
||||
"title": "zone_transfer_request",
|
||||
"description": "Zone Transfer Request",
|
||||
"additionalProperties": false,
|
||||
|
||||
"required": ["transfer_request"],
|
||||
|
||||
"properties": {
|
||||
"transfer_request": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["zone_id"],
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Zone Transfer Request identifier",
|
||||
"pattern": "^([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
|
||||
},
|
||||
"zone_id": {
|
||||
"type": "string",
|
||||
"description": "Zone identifier",
|
||||
"pattern": "^([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
|
||||
},
|
||||
"zone_name": {
|
||||
"type": ["string","null"],
|
||||
"description": "Zone Name for the Request",
|
||||
"maxLength": 255,
|
||||
"readOnly": true
|
||||
},
|
||||
"target_project_id": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Tenant identifier",
|
||||
"maxLength": 160
|
||||
},
|
||||
"project_id": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Project identifier",
|
||||
"maxLength": 36,
|
||||
"immutable": true
|
||||
},
|
||||
"description": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Description for the Request",
|
||||
"maxLength": 160
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Password used to complete the transfer",
|
||||
"maxLength": 160
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "Zone Status",
|
||||
"enum": ["ACTIVE", "PENDING", "DELETED", "ERROR", "COMPLETE"],
|
||||
"readOnly": true
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"description": "Date and time of Request creation",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
},
|
||||
"updated_at": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Date and time of last Request modification",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"self": {
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
designate/resources/schemas/v2/transfer_requests.json
Normal file
38
designate/resources/schemas/v2/transfer_requests.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/hyper-schema",
|
||||
|
||||
"id": "zone_transfer_request",
|
||||
|
||||
"title": "zone_transfer_request",
|
||||
"description": "Zone Transfer Request",
|
||||
"additionalProperties": false,
|
||||
|
||||
"required": ["transfer_requests"],
|
||||
|
||||
"properties": {
|
||||
"transfer_request": {
|
||||
"type": "array",
|
||||
"description": "Zone Transfer Requests",
|
||||
"items": {"$ref": "transfer_request#/properties/transfer_request"}
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"self": {
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"next": {
|
||||
"type": ["string", "null"],
|
||||
"format": "url"
|
||||
},
|
||||
"previous": {
|
||||
"type": ["string", "null"],
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -179,7 +179,7 @@ class SQLAlchemy(object):
|
||||
|
||||
def _find(self, context, table, cls, list_cls, exc_notfound, criterion,
|
||||
one=False, marker=None, limit=None, sort_key=None,
|
||||
sort_dir=None, query=None):
|
||||
sort_dir=None, query=None, apply_tenant_criteria=True):
|
||||
sort_key = sort_key or 'created_at'
|
||||
sort_dir = sort_dir or 'asc'
|
||||
|
||||
@ -187,7 +187,8 @@ class SQLAlchemy(object):
|
||||
if query is None:
|
||||
query = select([table])
|
||||
query = self._apply_criterion(table, query, criterion)
|
||||
query = self._apply_tenant_criteria(context, table, query)
|
||||
if apply_tenant_criteria:
|
||||
query = self._apply_tenant_criteria(context, table, query)
|
||||
query = self._apply_deleted_criteria(context, table, query)
|
||||
|
||||
# Execute the Query
|
||||
|
@ -19,6 +19,7 @@ import hashlib
|
||||
from oslo.config import cfg
|
||||
from oslo.db import options
|
||||
from sqlalchemy import select, distinct, func
|
||||
from sqlalchemy.sql.expression import or_
|
||||
|
||||
from designate.openstack.common import log as logging
|
||||
from designate import exceptions
|
||||
@ -287,10 +288,29 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
|
||||
|
||||
def update_domain(self, context, domain):
|
||||
# Don't handle recordsets for now
|
||||
return self._update(
|
||||
|
||||
tenant_id_changed = False
|
||||
if 'tenant_id' in domain.obj_what_changed():
|
||||
tenant_id_changed = True
|
||||
|
||||
updated_domain = self._update(
|
||||
context, tables.domains, domain, exceptions.DuplicateDomain,
|
||||
exceptions.DomainNotFound, ['recordsets'])
|
||||
|
||||
if tenant_id_changed:
|
||||
recordsets_query = tables.recordsets.update().\
|
||||
where(tables.recordsets.c.domain_id == domain.id)\
|
||||
.values({'tenant_id': domain.tenant_id})
|
||||
|
||||
records_query = tables.records.update().\
|
||||
where(tables.records.c.domain_id == domain.id).\
|
||||
values({'tenant_id': domain.tenant_id})
|
||||
|
||||
self.session.execute(records_query)
|
||||
self.session.execute(recordsets_query)
|
||||
|
||||
return updated_domain
|
||||
|
||||
def delete_domain(self, context, domain_id):
|
||||
# Fetch the existing domain, we'll need to return it.
|
||||
domain = self._find_domains(context, {'id': domain_id}, one=True)
|
||||
@ -735,6 +755,169 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
|
||||
|
||||
return deleted_pool_attribute
|
||||
|
||||
# Zone Transfer Methods
|
||||
def _find_zone_transfer_requests(self, context, criterion, one=False,
|
||||
marker=None, limit=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
|
||||
table = tables.zone_transfer_requests
|
||||
|
||||
ljoin = tables.zone_transfer_requests.join(
|
||||
tables.domains,
|
||||
tables.zone_transfer_requests.c.domain_id == tables.domains.c.id)
|
||||
|
||||
query = select(
|
||||
[table, tables.domains.c.name.label("domain_name")]
|
||||
).select_from(ljoin)
|
||||
|
||||
if context.all_tenants:
|
||||
LOG.debug('Including all tenants items in query results')
|
||||
else:
|
||||
query = query.where(or_(
|
||||
table.c.tenant_id == context.tenant,
|
||||
table.c.target_tenant_id == context.tenant))
|
||||
|
||||
return self._find(
|
||||
context, table, objects.ZoneTransferRequest,
|
||||
objects.ZoneTransferRequestList,
|
||||
exceptions.ZoneTransferRequestNotFound,
|
||||
criterion,
|
||||
one=one, marker=marker, limit=limit, sort_dir=sort_dir,
|
||||
sort_key=sort_key, query=query, apply_tenant_criteria=False
|
||||
)
|
||||
|
||||
def create_zone_transfer_request(self, context, zone_transfer_request):
|
||||
|
||||
try:
|
||||
criterion = {"domain_id": zone_transfer_request.domain_id,
|
||||
"status": "ACTIVE"}
|
||||
self.find_zone_transfer_request(
|
||||
context, criterion)
|
||||
except exceptions.ZoneTransferRequestNotFound:
|
||||
return self._create(
|
||||
tables.zone_transfer_requests,
|
||||
zone_transfer_request,
|
||||
exceptions.DuplicateZoneTransferRequest)
|
||||
else:
|
||||
raise exceptions.DuplicateZoneTransferRequest()
|
||||
|
||||
def find_zone_transfer_requests(self, context, criterion=None,
|
||||
marker=None, limit=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
|
||||
return self._find_zone_transfer_requests(
|
||||
context, criterion, marker=marker,
|
||||
limit=limit, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
def get_zone_transfer_request(self, context, zone_transfer_request_id):
|
||||
request = self._find_zone_transfer_requests(
|
||||
context,
|
||||
{'id': zone_transfer_request_id},
|
||||
one=True)
|
||||
|
||||
return request
|
||||
|
||||
def find_zone_transfer_request(self, context, criterion):
|
||||
|
||||
return self._find_zone_transfer_requests(context, criterion, one=True)
|
||||
|
||||
def update_zone_transfer_request(self, context, zone_transfer_request):
|
||||
|
||||
zone_transfer_request.obj_reset_changes(('domain_name'))
|
||||
|
||||
updated_zt_request = self._update(
|
||||
context,
|
||||
tables.zone_transfer_requests,
|
||||
zone_transfer_request,
|
||||
exceptions.DuplicateZoneTransferRequest,
|
||||
exceptions.ZoneTransferRequestNotFound,
|
||||
skip_values=['domain_name'])
|
||||
|
||||
return updated_zt_request
|
||||
|
||||
def delete_zone_transfer_request(self, context, zone_transfer_request_id):
|
||||
|
||||
zone_transfer_request = self._find_zone_transfer_requests(
|
||||
context,
|
||||
{'id': zone_transfer_request_id},
|
||||
one=True)
|
||||
|
||||
return self._delete(
|
||||
context,
|
||||
tables.zone_transfer_requests,
|
||||
zone_transfer_request,
|
||||
exceptions.ZoneTransferRequestNotFound)
|
||||
|
||||
def _find_zone_transfer_accept(self, context, criterion, one=False,
|
||||
marker=None, limit=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
|
||||
return self._find(
|
||||
context, tables.zone_transfer_accepts,
|
||||
objects.ZoneTransferAccept,
|
||||
objects.ZoneTransferAcceptList,
|
||||
exceptions.ZoneTransferAcceptNotFound, criterion,
|
||||
one, marker, limit, sort_key, sort_dir)
|
||||
|
||||
def _get_domain_name(self, context, domain_id, all_tenants=False):
|
||||
|
||||
if all_tenants:
|
||||
ctxt = context.elevated()
|
||||
ctxt.all_tenants = True
|
||||
else:
|
||||
ctxt = context
|
||||
|
||||
return self.get_domain(ctxt, domain_id).name
|
||||
|
||||
def create_zone_transfer_accept(self, context, zone_transfer_accept):
|
||||
|
||||
return self._create(
|
||||
tables.zone_transfer_accepts,
|
||||
zone_transfer_accept,
|
||||
exceptions.DuplicateZoneTransferAccept)
|
||||
|
||||
def find_zone_transfer_accepts(self, context, criterion=None,
|
||||
marker=None, limit=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
return self._find_zone_transfer_accept(
|
||||
context, criterion, marker=marker, limit=limit, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
def get_zone_transfer_accept(self, context, zone_transfer_accept_id):
|
||||
return self._find_zone_transfer_accept(
|
||||
context,
|
||||
{'id': zone_transfer_accept_id},
|
||||
one=True)
|
||||
|
||||
def find_zone_transfer_accept(self, context, criterion):
|
||||
return self._find_zone_transfer_accept(
|
||||
context,
|
||||
criterion,
|
||||
one=True)
|
||||
|
||||
def update_zone_transfer_accept(self, context, zone_transfer_accept):
|
||||
|
||||
return self._update(
|
||||
context,
|
||||
tables.zone_transfer_accepts,
|
||||
zone_transfer_accept,
|
||||
exceptions.DuplicateZoneTransferAccept,
|
||||
exceptions.ZoneTransferAcceptNotFound)
|
||||
|
||||
def delete_zone_transfer_accept(self, context, zone_transfer_accept_id):
|
||||
|
||||
zone_transfer_accept = self._find_zone_transfer_accept(
|
||||
context,
|
||||
{'id': zone_transfer_accept_id},
|
||||
one=True)
|
||||
|
||||
return self._delete(
|
||||
context,
|
||||
tables.zone_transfer_accepts,
|
||||
zone_transfer_accept,
|
||||
exceptions.ZoneTransferAcceptNotFound)
|
||||
|
||||
# diagnostics
|
||||
def ping(self, context):
|
||||
start_time = time.time()
|
||||
|
@ -0,0 +1,75 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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 sqlalchemy import Integer, String, DateTime, Enum, ForeignKey
|
||||
from sqlalchemy.schema import Table, Column, MetaData
|
||||
|
||||
from designate.sqlalchemy.types import UUID
|
||||
|
||||
TASK_STATUSES = ['ACTIVE', 'PENDING', 'DELETED', 'ERROR', 'COMPLETE']
|
||||
|
||||
meta = MetaData()
|
||||
|
||||
zone_transfer_requests = Table(
|
||||
'zone_transfer_requests',
|
||||
meta,
|
||||
Column('id', UUID(), primary_key=True),
|
||||
Column('domain_id', UUID, ForeignKey('domains.id'), nullable=False),
|
||||
Column('key', String(255), nullable=False),
|
||||
Column('description', String(255), nullable=True),
|
||||
Column('tenant_id', String(36), nullable=False),
|
||||
Column('target_tenant_id', String(36), nullable=True),
|
||||
Column('status', Enum(name='resource_statuses', *TASK_STATUSES),
|
||||
nullable=False, server_default='ACTIVE',),
|
||||
Column('created_at', DateTime()),
|
||||
Column('updated_at', DateTime()),
|
||||
Column('version', Integer(), nullable=False),
|
||||
mysql_engine='INNODB',
|
||||
mysql_charset='utf8'
|
||||
)
|
||||
|
||||
zone_transfer_accepts = Table(
|
||||
'zone_transfer_accepts',
|
||||
meta,
|
||||
Column('id', UUID(), primary_key=True),
|
||||
Column('domain_id', UUID, ForeignKey('domains.id'), nullable=False),
|
||||
Column('zone_transfer_request_id', UUID,
|
||||
ForeignKey('zone_transfer_requests.id',
|
||||
ondelete='CASCADE'),
|
||||
nullable=False),
|
||||
Column('status', Enum(name='resource_statuses', *TASK_STATUSES),
|
||||
nullable=False, server_default='ACTIVE'),
|
||||
Column('tenant_id', String(36), nullable=False),
|
||||
Column('created_at', DateTime()),
|
||||
Column('updated_at', DateTime()),
|
||||
Column('version', Integer(), nullable=False),
|
||||
mysql_engine='INNODB',
|
||||
mysql_charset='utf8'
|
||||
)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
Table('domains', meta, autoload=True)
|
||||
|
||||
zone_transfer_requests.create()
|
||||
zone_transfer_accepts.create()
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
|
||||
zone_transfer_accepts.drop()
|
||||
zone_transfer_requests.drop()
|
@ -29,6 +29,7 @@ CONF = cfg.CONF
|
||||
RESOURCE_STATUSES = ['ACTIVE', 'PENDING', 'DELETED', 'ERROR']
|
||||
RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'SPF', 'NS', 'PTR',
|
||||
'SSHFP']
|
||||
TASK_STATUSES = ['ACTIVE', 'PENDING', 'DELETED', 'ERROR', 'COMPLETE']
|
||||
TSIG_ALGORITHMS = ['hmac-md5', 'hmac-sha1', 'hmac-sha224', 'hmac-sha256',
|
||||
'hmac-sha384', 'hmac-sha512']
|
||||
POOL_PROVISIONERS = ['UNMANAGED']
|
||||
@ -232,3 +233,47 @@ pool_attributes = Table('pool_attributes', metadata,
|
||||
mysql_engine='INNODB',
|
||||
mysql_charset='utf8'
|
||||
)
|
||||
|
||||
zone_transfer_requests = Table('zone_transfer_requests', metadata,
|
||||
Column('id', UUID, default=utils.generate_uuid, primary_key=True),
|
||||
Column('version', Integer(), default=1, nullable=False),
|
||||
Column('created_at', DateTime, default=lambda: timeutils.utcnow()),
|
||||
Column('updated_at', DateTime, onupdate=lambda: timeutils.utcnow()),
|
||||
|
||||
Column('domain_id', UUID, nullable=False),
|
||||
Column("key", String(255), nullable=False),
|
||||
Column("description", String(255), nullable=False),
|
||||
Column("tenant_id", String(36), default=None, nullable=False),
|
||||
Column("target_tenant_id", String(36), default=None, nullable=True),
|
||||
Column("status", Enum(name='resource_statuses', *TASK_STATUSES),
|
||||
nullable=False, server_default='ACTIVE',
|
||||
default='ACTIVE'),
|
||||
|
||||
ForeignKeyConstraint(['domain_id'], ['domains.id'], ondelete='CASCADE'),
|
||||
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8',
|
||||
)
|
||||
|
||||
zone_transfer_accepts = Table('zone_transfer_accepts', metadata,
|
||||
Column('id', UUID, default=utils.generate_uuid, primary_key=True),
|
||||
Column('version', Integer(), default=1, nullable=False),
|
||||
Column('created_at', DateTime, default=lambda: timeutils.utcnow()),
|
||||
Column('updated_at', DateTime, onupdate=lambda: timeutils.utcnow()),
|
||||
|
||||
Column('domain_id', UUID, nullable=False),
|
||||
Column('zone_transfer_request_id', UUID, nullable=False),
|
||||
Column("tenant_id", String(36), default=None, nullable=False),
|
||||
Column("status", Enum(name='resource_statuses', *TASK_STATUSES),
|
||||
nullable=False, server_default='ACTIVE',
|
||||
default='ACTIVE'),
|
||||
|
||||
ForeignKeyConstraint(['domain_id'], ['domains.id'], ondelete='CASCADE'),
|
||||
ForeignKeyConstraint(
|
||||
['zone_transfer_request_id'],
|
||||
['zone_transfer_requests.id'],
|
||||
ondelete='CASCADE'),
|
||||
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8',
|
||||
)
|
||||
|
@ -279,6 +279,13 @@ class TestCase(base.BaseTestCase):
|
||||
['examplens1.org', 'examplens2.org']
|
||||
]
|
||||
|
||||
zone_transfers_request_fixtures = [{
|
||||
"description": "Test Transfer",
|
||||
}, {
|
||||
"description": "Test Transfer 2 - with target",
|
||||
"target_tenant_id": "target_tenant_id"
|
||||
}]
|
||||
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
|
||||
@ -525,6 +532,20 @@ class TestCase(base.BaseTestCase):
|
||||
_values = copy.copy(self.name_server_fixtures[fixture])
|
||||
return _values
|
||||
|
||||
def get_zone_transfer_request_fixture(self, fixture=0, values=None):
|
||||
values = values or {}
|
||||
|
||||
_values = copy.copy(self.zone_transfers_request_fixtures[fixture])
|
||||
_values.update(values)
|
||||
return _values
|
||||
|
||||
def get_zone_transfer_accept_fixture(self, fixture=0, values=None):
|
||||
values = values or {}
|
||||
|
||||
_values = copy.copy(self.zone_transfers_accept_fixtures[fixture])
|
||||
_values.update(values)
|
||||
return _values
|
||||
|
||||
def create_server(self, **kwargs):
|
||||
context = kwargs.pop('context', self.admin_context)
|
||||
fixture = kwargs.pop('fixture', 0)
|
||||
@ -625,6 +646,43 @@ class TestCase(base.BaseTestCase):
|
||||
return self.central_service.create_pool(
|
||||
context, objects.Pool(**values))
|
||||
|
||||
def create_zone_transfer_request(self, domain, **kwargs):
|
||||
context = kwargs.pop('context', self.admin_context)
|
||||
fixture = kwargs.pop('fixture', 0)
|
||||
|
||||
values = self.get_zone_transfer_request_fixture(
|
||||
fixture=fixture, values=kwargs)
|
||||
|
||||
if 'domain_id' not in values:
|
||||
values['domain_id'] = domain.id
|
||||
|
||||
zone_transfer_request = objects.ZoneTransferRequest(**values)
|
||||
|
||||
return self.central_service.create_zone_transfer_request(
|
||||
context, zone_transfer_request=zone_transfer_request)
|
||||
|
||||
def create_zone_transfer_accept(self, zone_transfer_request, **kwargs):
|
||||
context = kwargs.pop('context', self.admin_context)
|
||||
|
||||
values = {}
|
||||
|
||||
if 'tenant_id' not in values:
|
||||
values['tenant_id'] = context.tenant
|
||||
|
||||
if 'zone_transfer_request_id' not in values:
|
||||
values['zone_transfer_request_id'] = zone_transfer_request.id
|
||||
|
||||
if 'domain_id' not in values:
|
||||
values['domain_id'] = zone_transfer_request.domain_id
|
||||
|
||||
if 'key' not in values:
|
||||
values['key'] = zone_transfer_request.key
|
||||
|
||||
zone_transfer_accept = objects.ZoneTransferAccept(**values)
|
||||
|
||||
return self.central_service.create_zone_transfer_accept(
|
||||
context, zone_transfer_accept)
|
||||
|
||||
def _ensure_interface(self, interface, implementation):
|
||||
for name in interface.__abstractmethods__:
|
||||
in_arginfo = inspect.getargspec(getattr(interface, name))
|
||||
|
181
designate/tests/test_api/test_v2/test_zone_transfers.py
Normal file
181
designate/tests/test_api/test_v2/test_zone_transfers.py
Normal file
@ -0,0 +1,181 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Graham Hayes <graham.hayes@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.tests.test_api.test_v2 import ApiV2TestCase
|
||||
|
||||
|
||||
class ApiV2ZoneTransfersTest(ApiV2TestCase):
|
||||
def setUp(self):
|
||||
super(ApiV2ZoneTransfersTest, self).setUp()
|
||||
|
||||
self.domain = self.create_domain()
|
||||
self.tenant_1_context = self.get_context(tenant=1)
|
||||
self.tenant_2_context = self.get_context(tenant=2)
|
||||
|
||||
def test_create_zone_transfer_request(self):
|
||||
response = self.client.post_json(
|
||||
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
|
||||
{'transfer_request': {}})
|
||||
|
||||
# Check the headers are what we expect
|
||||
self.assertEqual(201, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
|
||||
# Check the body structure is what we expect
|
||||
self.assertIn('transfer_request', response.json)
|
||||
self.assertIn('links', response.json['transfer_request'])
|
||||
self.assertIn('self', response.json['transfer_request']['links'])
|
||||
|
||||
# Check the values returned are what we expect
|
||||
self.assertIn('id', response.json['transfer_request'])
|
||||
self.assertIn('created_at', response.json['transfer_request'])
|
||||
self.assertEqual('ACTIVE', response.json['transfer_request']['status'])
|
||||
self.assertEqual(
|
||||
self.domain.id,
|
||||
response.json['transfer_request']['zone_id'])
|
||||
self.assertIsNone(response.json['transfer_request']['updated_at'])
|
||||
|
||||
def test_create_zone_transfer_request_scoped(self):
|
||||
response = self.client.post_json(
|
||||
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
|
||||
{'transfer_request':
|
||||
{'target_project_id': str(self.tenant_1_context.tenant)}})
|
||||
|
||||
# Check the headers are what we expect
|
||||
self.assertEqual(201, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
|
||||
# Check the body structure is what we expect
|
||||
self.assertIn('transfer_request', response.json)
|
||||
self.assertIn('links', response.json['transfer_request'])
|
||||
self.assertIn('self', response.json['transfer_request']['links'])
|
||||
|
||||
# Check the values returned are what we expect
|
||||
self.assertIn('id', response.json['transfer_request'])
|
||||
self.assertIn('created_at', response.json['transfer_request'])
|
||||
self.assertEqual('ACTIVE', response.json['transfer_request']['status'])
|
||||
self.assertEqual(
|
||||
str(self.tenant_1_context.tenant),
|
||||
response.json['transfer_request']['target_project_id'])
|
||||
self.assertEqual(
|
||||
self.domain.id,
|
||||
response.json['transfer_request']['zone_id'])
|
||||
self.assertIsNone(response.json['transfer_request']['updated_at'])
|
||||
|
||||
def test_get_zone_transfer_request(self):
|
||||
initial = self.client.post_json(
|
||||
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
|
||||
{'transfer_request': {}})
|
||||
|
||||
response = self.client.get(
|
||||
'/zones/tasks/transfer_requests/%s' %
|
||||
(initial.json['transfer_request']['id']))
|
||||
|
||||
# Check the headers are what we expect
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
|
||||
# Check the body structure is what we expect
|
||||
self.assertIn('transfer_request', response.json)
|
||||
self.assertIn('links', response.json['transfer_request'])
|
||||
self.assertIn('self', response.json['transfer_request']['links'])
|
||||
|
||||
# Check the values returned are what we expect
|
||||
self.assertIn('id', response.json['transfer_request'])
|
||||
self.assertIn('created_at', response.json['transfer_request'])
|
||||
self.assertEqual('ACTIVE', response.json['transfer_request']['status'])
|
||||
self.assertEqual(
|
||||
self.domain.id,
|
||||
response.json['transfer_request']['zone_id'])
|
||||
self.assertIn('updated_at', response.json['transfer_request'])
|
||||
|
||||
def test_update_zone_transfer_request(self):
|
||||
initial = self.client.post_json(
|
||||
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
|
||||
{'transfer_request': {}})
|
||||
|
||||
response = self.client.patch_json(
|
||||
'/zones/tasks/transfer_requests/%s' %
|
||||
(initial.json['transfer_request']['id']),
|
||||
{'transfer_request': {"description": "TEST"}})
|
||||
|
||||
# Check the headers are what we expect
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
|
||||
# Check the body structure is what we expect
|
||||
self.assertIn('transfer_request', response.json)
|
||||
self.assertIn('links', response.json['transfer_request'])
|
||||
self.assertIn('self', response.json['transfer_request']['links'])
|
||||
|
||||
# Check the values returned are what we expect
|
||||
self.assertIn('id', response.json['transfer_request'])
|
||||
self.assertIn('created_at', response.json['transfer_request'])
|
||||
self.assertEqual('ACTIVE', response.json['transfer_request']['status'])
|
||||
self.assertEqual(
|
||||
self.domain.id,
|
||||
response.json['transfer_request']['zone_id'])
|
||||
self.assertEqual(
|
||||
'TEST', response.json['transfer_request']['description'])
|
||||
self.assertIn('updated_at', response.json['transfer_request'])
|
||||
|
||||
def test_delete_zone_transfer_request(self):
|
||||
initial = self.client.post_json(
|
||||
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
|
||||
{'transfer_request': {}})
|
||||
|
||||
response = self.client.delete(
|
||||
'/zones/tasks/transfer_requests/%s' %
|
||||
(initial.json['transfer_request']['id']))
|
||||
|
||||
# Check the headers are what we expect
|
||||
self.assertEqual(204, response.status_int)
|
||||
|
||||
def test_create_zone_transfer_accept(self):
|
||||
initial = self.client.post_json(
|
||||
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
|
||||
{'transfer_request': {}})
|
||||
|
||||
response = self.client.post_json(
|
||||
'/zones/tasks/transfer_accepts',
|
||||
{'transfer_accept': {
|
||||
'zone_transfer_request_id':
|
||||
initial.json['transfer_request']['id'],
|
||||
'key': initial.json['transfer_request']['key']
|
||||
}})
|
||||
|
||||
new_ztr = self.client.get(
|
||||
'/zones/tasks/transfer_requests/%s' %
|
||||
(initial.json['transfer_request']['id']))
|
||||
|
||||
# Check the headers are what we expect
|
||||
self.assertEqual(201, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
|
||||
# Check the body structure is what we expect
|
||||
self.assertIn('transfer_accept', response.json)
|
||||
self.assertIn('links', response.json['transfer_accept'])
|
||||
self.assertIn('self', response.json['transfer_accept']['links'])
|
||||
self.assertIn('zone', response.json['transfer_accept']['links'])
|
||||
|
||||
# Check the values returned are what we expect
|
||||
self.assertIn('id', response.json['transfer_accept'])
|
||||
self.assertEqual(
|
||||
'COMPLETE',
|
||||
response.json['transfer_accept']['status'])
|
||||
|
||||
self.assertEqual(
|
||||
'COMPLETE',
|
||||
new_ztr.json['transfer_request']['status'])
|
@ -2473,3 +2473,246 @@ class CentralServiceTest(CentralTestCase):
|
||||
# Verify that the pool has been deleted
|
||||
with testtools.ExpectedException(exceptions.PoolNotFound):
|
||||
self.central_service.get_pool(self.admin_context, pool['id'])
|
||||
|
||||
def test_create_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
zone_transfer_request = self.create_zone_transfer_request(domain)
|
||||
|
||||
# Verify all values have been set correctly
|
||||
self.assertIsNotNone(zone_transfer_request.id)
|
||||
self.assertIsNotNone(zone_transfer_request.tenant_id)
|
||||
self.assertIsNotNone(zone_transfer_request.key)
|
||||
self.assertEqual(zone_transfer_request.domain_id, domain.id)
|
||||
|
||||
def test_create_zone_transfer_request_duplicate(self):
|
||||
domain = self.create_domain()
|
||||
self.create_zone_transfer_request(domain)
|
||||
with testtools.ExpectedException(
|
||||
exceptions.DuplicateZoneTransferRequest):
|
||||
self.create_zone_transfer_request(domain)
|
||||
|
||||
def test_create_scoped_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
values = self.get_zone_transfer_request_fixture(fixture=1)
|
||||
zone_transfer_request = self.create_zone_transfer_request(domain,
|
||||
fixture=1)
|
||||
|
||||
# Verify all values have been set correctly
|
||||
self.assertIsNotNone(zone_transfer_request.id)
|
||||
self.assertIsNotNone(zone_transfer_request.tenant_id)
|
||||
self.assertEqual(zone_transfer_request.domain_id, domain.id)
|
||||
self.assertIsNotNone(zone_transfer_request.key)
|
||||
self.assertEqual(
|
||||
zone_transfer_request.target_tenant_id,
|
||||
values['target_tenant_id'])
|
||||
|
||||
def test_get_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
zt_request = self.create_zone_transfer_request(domain,
|
||||
fixture=1)
|
||||
retrived_zt = self.central_service.get_zone_transfer_request(
|
||||
self.admin_context,
|
||||
zt_request.id)
|
||||
self.assertEqual(zt_request.domain_id, retrived_zt.domain_id)
|
||||
self.assertEqual(zt_request.key, retrived_zt.key)
|
||||
|
||||
def test_get_zone_transfer_request_scoped(self):
|
||||
tenant_1_context = self.get_context(tenant=1)
|
||||
tenant_2_context = self.get_context(tenant=2)
|
||||
tenant_3_context = self.get_context(tenant=3)
|
||||
domain = self.create_domain(context=tenant_1_context)
|
||||
zt_request = self.create_zone_transfer_request(
|
||||
domain,
|
||||
context=tenant_1_context,
|
||||
target_tenant_id=2)
|
||||
|
||||
self.central_service.get_zone_transfer_request(
|
||||
tenant_2_context, zt_request.id)
|
||||
|
||||
self.central_service.get_zone_transfer_request(
|
||||
tenant_1_context, zt_request.id)
|
||||
|
||||
with testtools.ExpectedException(exceptions.Forbidden):
|
||||
self.central_service.get_zone_transfer_request(
|
||||
tenant_3_context, zt_request.id)
|
||||
|
||||
def test_update_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
zone_transfer_request = self.create_zone_transfer_request(domain)
|
||||
|
||||
zone_transfer_request.description = 'TEST'
|
||||
self.central_service.update_zone_transfer_request(
|
||||
self.admin_context, zone_transfer_request)
|
||||
|
||||
# Verify all values have been set correctly
|
||||
self.assertIsNotNone(zone_transfer_request.id)
|
||||
self.assertIsNotNone(zone_transfer_request.tenant_id)
|
||||
self.assertIsNotNone(zone_transfer_request.key)
|
||||
self.assertEqual(zone_transfer_request.description, 'TEST')
|
||||
|
||||
def test_delete_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
zone_transfer_request = self.create_zone_transfer_request(domain)
|
||||
|
||||
self.central_service.delete_zone_transfer_request(
|
||||
self.admin_context, zone_transfer_request.id)
|
||||
|
||||
with testtools.ExpectedException(
|
||||
exceptions.ZoneTransferRequestNotFound):
|
||||
self.central_service.get_zone_transfer_request(
|
||||
self.admin_context,
|
||||
zone_transfer_request.id)
|
||||
|
||||
def test_create_zone_transfer_accept(self):
|
||||
tenant_1_context = self.get_context(tenant=1)
|
||||
tenant_2_context = self.get_context(tenant=2)
|
||||
admin_context = self.get_admin_context()
|
||||
admin_context.all_tenants = True
|
||||
|
||||
domain = self.create_domain(context=tenant_1_context)
|
||||
recordset = self.create_recordset(domain, context=tenant_1_context)
|
||||
record = self.create_record(
|
||||
domain, recordset, context=tenant_1_context)
|
||||
|
||||
zone_transfer_request = self.create_zone_transfer_request(
|
||||
domain, context=tenant_1_context)
|
||||
|
||||
zone_transfer_accept = objects.ZoneTransferAccept()
|
||||
zone_transfer_accept.zone_transfer_request_id =\
|
||||
zone_transfer_request.id
|
||||
|
||||
zone_transfer_accept.key = zone_transfer_request.key
|
||||
zone_transfer_accept.domain_id = domain.id
|
||||
|
||||
zone_transfer_accept = \
|
||||
self.central_service.create_zone_transfer_accept(
|
||||
tenant_2_context, zone_transfer_accept)
|
||||
|
||||
result = {}
|
||||
result['domain'] = self.central_service.get_domain(
|
||||
admin_context, domain.id)
|
||||
|
||||
result['recordset'] = self.central_service.get_recordset(
|
||||
admin_context, domain.id, recordset.id)
|
||||
|
||||
result['record'] = self.central_service.get_record(
|
||||
admin_context, domain.id, recordset.id, record.id)
|
||||
|
||||
result['zt_accept'] = self.central_service.get_zone_transfer_accept(
|
||||
admin_context, zone_transfer_accept.id)
|
||||
result['zt_request'] = self.central_service.get_zone_transfer_request(
|
||||
admin_context, zone_transfer_request.id)
|
||||
|
||||
self.assertEqual(
|
||||
result['domain'].tenant_id, str(tenant_2_context.tenant))
|
||||
self.assertEqual(
|
||||
result['recordset'].tenant_id, str(tenant_2_context.tenant))
|
||||
self.assertEqual(
|
||||
result['record'].tenant_id, str(tenant_2_context.tenant))
|
||||
self.assertEqual(
|
||||
result['zt_accept'].status, 'COMPLETE')
|
||||
self.assertEqual(
|
||||
result['zt_request'].status, 'COMPLETE')
|
||||
|
||||
def test_create_zone_transfer_accept_scoped(self):
|
||||
tenant_1_context = self.get_context(tenant=1)
|
||||
tenant_2_context = self.get_context(tenant=2)
|
||||
admin_context = self.get_admin_context()
|
||||
admin_context.all_tenants = True
|
||||
|
||||
domain = self.create_domain(context=tenant_1_context)
|
||||
recordset = self.create_recordset(domain, context=tenant_1_context)
|
||||
record = self.create_record(
|
||||
domain, recordset, context=tenant_1_context)
|
||||
|
||||
zone_transfer_request = self.create_zone_transfer_request(
|
||||
domain,
|
||||
context=tenant_1_context,
|
||||
target_tenant_id='2')
|
||||
|
||||
zone_transfer_accept = objects.ZoneTransferAccept()
|
||||
zone_transfer_accept.zone_transfer_request_id =\
|
||||
zone_transfer_request.id
|
||||
|
||||
zone_transfer_accept.key = zone_transfer_request.key
|
||||
zone_transfer_accept.domain_id = domain.id
|
||||
|
||||
zone_transfer_accept = \
|
||||
self.central_service.create_zone_transfer_accept(
|
||||
tenant_2_context, zone_transfer_accept)
|
||||
|
||||
result = {}
|
||||
result['domain'] = self.central_service.get_domain(
|
||||
admin_context, domain.id)
|
||||
|
||||
result['recordset'] = self.central_service.get_recordset(
|
||||
admin_context, domain.id, recordset.id)
|
||||
|
||||
result['record'] = self.central_service.get_record(
|
||||
admin_context, domain.id, recordset.id, record.id)
|
||||
|
||||
result['zt_accept'] = self.central_service.get_zone_transfer_accept(
|
||||
admin_context, zone_transfer_accept.id)
|
||||
result['zt_request'] = self.central_service.get_zone_transfer_request(
|
||||
admin_context, zone_transfer_request.id)
|
||||
|
||||
self.assertEqual(
|
||||
result['domain'].tenant_id, str(tenant_2_context.tenant))
|
||||
self.assertEqual(
|
||||
result['recordset'].tenant_id, str(tenant_2_context.tenant))
|
||||
self.assertEqual(
|
||||
result['record'].tenant_id, str(tenant_2_context.tenant))
|
||||
self.assertEqual(
|
||||
result['zt_accept'].status, 'COMPLETE')
|
||||
self.assertEqual(
|
||||
result['zt_request'].status, 'COMPLETE')
|
||||
|
||||
def test_create_zone_transfer_accept_failed_key(self):
|
||||
tenant_1_context = self.get_context(tenant=1)
|
||||
tenant_2_context = self.get_context(tenant=2)
|
||||
admin_context = self.get_admin_context()
|
||||
admin_context.all_tenants = True
|
||||
|
||||
domain = self.create_domain(context=tenant_1_context)
|
||||
|
||||
zone_transfer_request = self.create_zone_transfer_request(
|
||||
domain,
|
||||
context=tenant_1_context,
|
||||
target_tenant_id=2)
|
||||
|
||||
zone_transfer_accept = objects.ZoneTransferAccept()
|
||||
zone_transfer_accept.zone_transfer_request_id =\
|
||||
zone_transfer_request.id
|
||||
|
||||
zone_transfer_accept.key = 'WRONG KEY'
|
||||
zone_transfer_accept.domain_id = domain.id
|
||||
|
||||
with testtools.ExpectedException(exceptions.IncorrectZoneTransferKey):
|
||||
zone_transfer_accept = \
|
||||
self.central_service.create_zone_transfer_accept(
|
||||
tenant_2_context, zone_transfer_accept)
|
||||
|
||||
def test_create_zone_tarnsfer_accept_out_of_tenant_scope(self):
|
||||
tenant_1_context = self.get_context(tenant=1)
|
||||
tenant_3_context = self.get_context(tenant=3)
|
||||
admin_context = self.get_admin_context()
|
||||
admin_context.all_tenants = True
|
||||
|
||||
domain = self.create_domain(context=tenant_1_context)
|
||||
|
||||
zone_transfer_request = self.create_zone_transfer_request(
|
||||
domain,
|
||||
context=tenant_1_context,
|
||||
target_tenant_id=2)
|
||||
|
||||
zone_transfer_accept = objects.ZoneTransferAccept()
|
||||
zone_transfer_accept.zone_transfer_request_id =\
|
||||
zone_transfer_request.id
|
||||
|
||||
zone_transfer_accept.key = zone_transfer_request.key
|
||||
zone_transfer_accept.domain_id = domain.id
|
||||
|
||||
with testtools.ExpectedException(exceptions.Forbidden):
|
||||
zone_transfer_accept = \
|
||||
self.central_service.create_zone_transfer_accept(
|
||||
tenant_3_context, zone_transfer_accept)
|
||||
|
@ -1986,3 +1986,164 @@ class StorageTestCase(object):
|
||||
with testtools.ExpectedException(exceptions.PoolNotFound):
|
||||
uuid = '203ca44f-c7e7-4337-9a02-0d735833e6aa'
|
||||
self.storage.delete_pool(self.admin_context, uuid)
|
||||
|
||||
def test_create_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
|
||||
values = {
|
||||
'tenant_id': self.admin_context.tenant,
|
||||
'domain_id': domain.id,
|
||||
'key': 'qwertyuiop'
|
||||
}
|
||||
|
||||
result = self.storage.create_zone_transfer_request(
|
||||
self.admin_context, objects.ZoneTransferRequest(**values))
|
||||
|
||||
self.assertEqual(result['tenant_id'], self.admin_context.tenant)
|
||||
self.assertIn('status', result)
|
||||
|
||||
def test_create_zone_transfer_request_scoped(self):
|
||||
domain = self.create_domain()
|
||||
tenant_2_context = self.get_context(tenant='2')
|
||||
tenant_3_context = self.get_context(tenant='3')
|
||||
|
||||
values = {
|
||||
'tenant_id': self.admin_context.tenant,
|
||||
'domain_id': domain.id,
|
||||
'key': 'qwertyuiop',
|
||||
'target_tenant_id': tenant_2_context.tenant,
|
||||
}
|
||||
|
||||
result = self.storage.create_zone_transfer_request(
|
||||
self.admin_context, objects.ZoneTransferRequest(**values))
|
||||
|
||||
self.assertIsNotNone(result['id'])
|
||||
self.assertIsNotNone(result['created_at'])
|
||||
self.assertIsNone(result['updated_at'])
|
||||
|
||||
self.assertEqual(result['tenant_id'], self.admin_context.tenant)
|
||||
self.assertEqual(result['target_tenant_id'], tenant_2_context.tenant)
|
||||
self.assertIn('status', result)
|
||||
|
||||
stored_ztr = self.storage.get_zone_transfer_request(
|
||||
tenant_2_context, result.id)
|
||||
|
||||
self.assertEqual(stored_ztr['tenant_id'], self.admin_context.tenant)
|
||||
self.assertEqual(result['id'], stored_ztr['id'])
|
||||
|
||||
with testtools.ExpectedException(
|
||||
exceptions.ZoneTransferRequestNotFound):
|
||||
self.storage.get_zone_transfer_request(
|
||||
tenant_3_context, result.id)
|
||||
|
||||
def test_delete_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
zt_request = self.create_zone_transfer_request(domain)
|
||||
|
||||
self.storage.delete_zone_transfer_request(
|
||||
self.admin_context, zt_request.id)
|
||||
|
||||
with testtools.ExpectedException(
|
||||
exceptions.ZoneTransferRequestNotFound):
|
||||
self.storage.get_zone_transfer_request(
|
||||
self.admin_context, zt_request.id)
|
||||
|
||||
def test_update_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
zt_request = self.create_zone_transfer_request(domain)
|
||||
|
||||
zt_request.description = 'New description'
|
||||
result = self.storage.update_zone_transfer_request(
|
||||
self.admin_context, zt_request)
|
||||
self.assertEqual(result.description, 'New description')
|
||||
|
||||
def test_get_zone_transfer_request(self):
|
||||
domain = self.create_domain()
|
||||
zt_request = self.create_zone_transfer_request(domain)
|
||||
|
||||
result = self.storage.get_zone_transfer_request(
|
||||
self.admin_context, zt_request.id)
|
||||
self.assertEqual(result.id, zt_request.id)
|
||||
self.assertEqual(result.domain_id, zt_request.domain_id)
|
||||
|
||||
def test_create_zone_transfer_accept(self):
|
||||
domain = self.create_domain()
|
||||
zt_request = self.create_zone_transfer_request(domain)
|
||||
values = {
|
||||
'tenant_id': self.admin_context.tenant,
|
||||
'zone_transfer_request_id': zt_request.id,
|
||||
'domain_id': domain.id,
|
||||
'key': zt_request.key
|
||||
}
|
||||
|
||||
result = self.storage.create_zone_transfer_accept(
|
||||
self.admin_context, objects.ZoneTransferAccept(**values))
|
||||
|
||||
self.assertIsNotNone(result['id'])
|
||||
self.assertIsNotNone(result['created_at'])
|
||||
self.assertIsNone(result['updated_at'])
|
||||
|
||||
self.assertEqual(result['tenant_id'], self.admin_context.tenant)
|
||||
self.assertIn('status', result)
|
||||
|
||||
def test_transfer_zone_ownership(self):
|
||||
tenant_1_context = self.get_context(tenant='1')
|
||||
tenant_2_context = self.get_context(tenant='2')
|
||||
admin_context = self.get_admin_context()
|
||||
admin_context.all_tenants = True
|
||||
|
||||
domain = self.create_domain(context=tenant_1_context)
|
||||
recordset = self.create_recordset(domain, context=tenant_1_context)
|
||||
record = self.create_record(
|
||||
domain, recordset, context=tenant_1_context)
|
||||
|
||||
updated_domain = domain
|
||||
|
||||
updated_domain.tenant_id = tenant_2_context.tenant
|
||||
|
||||
self.storage.update_domain(
|
||||
admin_context, updated_domain)
|
||||
|
||||
saved_domain = self.storage.get_domain(
|
||||
admin_context, domain.id)
|
||||
saved_recordset = self.storage.get_recordset(
|
||||
admin_context, recordset.id)
|
||||
saved_record = self.storage.get_record(
|
||||
admin_context, record.id)
|
||||
|
||||
self.assertEqual(saved_domain.tenant_id, tenant_2_context.tenant)
|
||||
self.assertEqual(saved_recordset.tenant_id, tenant_2_context.tenant)
|
||||
self.assertEqual(saved_record.tenant_id, tenant_2_context.tenant)
|
||||
|
||||
def test_delete_zone_transfer_accept(self):
|
||||
domain = self.create_domain()
|
||||
zt_request = self.create_zone_transfer_request(domain)
|
||||
zt_accept = self.create_zone_transfer_accept(zt_request)
|
||||
|
||||
self.storage.delete_zone_transfer_accept(
|
||||
self.admin_context, zt_accept.id)
|
||||
|
||||
with testtools.ExpectedException(
|
||||
exceptions.ZoneTransferAcceptNotFound):
|
||||
self.storage.get_zone_transfer_accept(
|
||||
self.admin_context, zt_accept.id)
|
||||
|
||||
def test_update_zone_transfer_accept(self):
|
||||
domain = self.create_domain()
|
||||
zt_request = self.create_zone_transfer_request(domain)
|
||||
zt_accept = self.create_zone_transfer_accept(zt_request)
|
||||
|
||||
zt_accept.status = 'COMPLETE'
|
||||
result = self.storage.update_zone_transfer_accept(
|
||||
self.admin_context, zt_accept)
|
||||
self.assertEqual(result.status, 'COMPLETE')
|
||||
|
||||
def test_get_zone_transfer_accept(self):
|
||||
domain = self.create_domain()
|
||||
zt_request = self.create_zone_transfer_request(domain)
|
||||
zt_accept = self.create_zone_transfer_accept(zt_request)
|
||||
|
||||
result = self.storage.get_zone_transfer_accept(
|
||||
self.admin_context, zt_accept.id)
|
||||
self.assertEqual(result.id, zt_accept.id)
|
||||
self.assertEqual(result.domain_id, zt_accept.domain_id)
|
||||
|
@ -15,9 +15,9 @@
|
||||
|
||||
.. _devstack:
|
||||
|
||||
========================
|
||||
========
|
||||
DevStack
|
||||
========================
|
||||
========
|
||||
|
||||
The Designate team maintains a fork of devstack with Designate integration.
|
||||
|
||||
@ -135,3 +135,4 @@ Instructions
|
||||
| type | A |
|
||||
| domain_id | 1fb5d17c-efaf-4e3c-aac0-482875d24b3e |
|
||||
+------------+--------------------------------------+
|
||||
|
||||
|
@ -285,7 +285,7 @@ Import Zone
|
||||
|
||||
.. http:post:: /zones
|
||||
|
||||
To import a zonefile, set the Content-type to **text/dns**. The
|
||||
To import a zonefile, set the Content-type to **text/dns** . The
|
||||
**zoneextractor.py** tool in the **contrib** folder can generate zonefiles
|
||||
that are suitable for Designate (without any **$INCLUDE** statements for
|
||||
example).
|
||||
@ -379,3 +379,213 @@ Export Zone
|
||||
:statuscode 406: Not Acceptable
|
||||
|
||||
Notice how the SOA and NS records are replaced with the Designate server(s).
|
||||
|
||||
Transfer Zone
|
||||
-------------
|
||||
|
||||
Create Zone Transfer Request
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:post:: /zones/(uuid:id)/tasks/transfer_requests
|
||||
|
||||
To initiate a transfer the original owner must create a transfer request.
|
||||
|
||||
This will return two items that are required to continue:
|
||||
* key: a password that is used to validate the transfer
|
||||
* id: ID of the request.
|
||||
|
||||
Both of these should be communicated out of band (email / IM / etc) to the intended recipient
|
||||
|
||||
There is an option of limiting the transfer to a single project. If that is required, the person initiating the transfer
|
||||
will need the Project ID. This will also allow the targeted project to see the transfer in their list of requests.
|
||||
|
||||
A non-targeted request will not show in a list operation, apart from the owning projects request.
|
||||
An targeted request will only show in the targets and owners lists.
|
||||
|
||||
An un-targeted request can be viewed by any authenticated user.
|
||||
|
||||
**Example Request**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /v2/zones/6b78734a-aef1-45cd-9708-8eb3c2d26ff8/tasks/transfer_requests HTTP/1.1
|
||||
Host: 127.0.0.1:9001
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"transfer_request":{
|
||||
"target_project_id": "123456",
|
||||
"description": "Transfer qa.dev.example.com. to QA Team"
|
||||
}
|
||||
}
|
||||
|
||||
**Example Response**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"transfer_request": {
|
||||
"created_at": "2014-07-17T20:34:40.882579",
|
||||
"description": null,
|
||||
"id": "f2ad17b5-807a-423f-a991-e06236c247be",
|
||||
"key": "9Z2R50Y0",
|
||||
"project_id": "1",
|
||||
"status": "ACTIVE",
|
||||
"target_project_id": "123456",
|
||||
"updated_at": null,
|
||||
"zone_id": "6b78734a-aef1-45cd-9708-8eb3c2d26ff8",
|
||||
"zone_name": "qa.dev.example.com.",
|
||||
"links": {
|
||||
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests/f2ad17b5-807a-423f-a991-e06236c247be"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:form description: UTF-8 text field
|
||||
:form target_project_id: Optional field to only allow a single tenant to accept the transfer request
|
||||
|
||||
|
||||
List Zone Transfer Requests
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:get:: /zones/tasks/transfer_requests
|
||||
|
||||
List all transfer requests that the requesting project have created, or are targeted to that project
|
||||
|
||||
The detail shown will differ, based on who the requester is.
|
||||
|
||||
**Example Request**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /zones/tasks/transfer_requests HTTP/1.1
|
||||
Host: 127.0.0.1:9001
|
||||
Accept: application/json
|
||||
|
||||
**Example Response**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"transfer_requests": [
|
||||
{
|
||||
"created_at": "2014-07-17T20:34:40.882579",
|
||||
"description": "This was created by the requesting project",
|
||||
"id": "f2ad17b5-807a-423f-a991-e06236c247be",
|
||||
"key": "9Z2R50Y0",
|
||||
"project_id": "1",
|
||||
"status": "ACTIVE",
|
||||
"target_project_id": "123456",
|
||||
"updated_at": null,
|
||||
"zone_id": "6b78734a-aef1-45cd-9708-8eb3c2d26ff8",
|
||||
"zone_name": "qa.dev.example.com.",
|
||||
"links": {
|
||||
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests/f2ad17b5-807a-423f-a991-e06236c247be"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "This is scoped to the requesting project",
|
||||
"id": "efd2d720-b0c4-43d4-99f7-d9b53e08860d",
|
||||
"zone_id": "2c4d5e37-f823-4bee-9859-031cb44f80e7",
|
||||
"zone_name": "subdomain.example.com.",
|
||||
"status": "ACTIVE",
|
||||
"links": {
|
||||
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests/efd2d720-b0c4-43d4-99f7-d9b53e08860d"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
View a Transfer Request
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:get:: /zones/tasks/transfer_requests/(uuid:id)
|
||||
|
||||
Show details about a request.
|
||||
|
||||
This allows a user to view a transfer request before accepting it
|
||||
|
||||
**Example Request**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /v2/zones/tasks/transfer_requests/f2ad17b5-807a-423f-a991-e06236c247be HTTP/1.1
|
||||
Host: 127.0.0.1:9001
|
||||
Accept: application/json
|
||||
|
||||
**Example Response**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"transfer_request":{
|
||||
"description": "This is scoped to the requesting project",
|
||||
"id": "efd2d720-b0c4-43d4-99f7-d9b53e08860d",
|
||||
"zone_id": "2c4d5e37-f823-4bee-9859-031cb44f80e7",
|
||||
"zone_name": "subdomain.example.com.",
|
||||
"status": "ACTIVE",
|
||||
"links": {
|
||||
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests/efd2d720-b0c4-43d4-99f7-d9b53e08860d"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Accept a Transfer Request
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:post:: /zones/tasks/transfer_accepts
|
||||
|
||||
Accept a zone transfer request. This is called by the project that will own the zone
|
||||
(i.e. the project that will maintain the zone)
|
||||
|
||||
Once the API returns "Complete" the zone has been transfered to the new project
|
||||
|
||||
**Example Request**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /v2/zones/tasks/transfer_accept HTTP/1.1
|
||||
Host: 127.0.0.1:9001
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"transfer_accept":{
|
||||
"key":"9Z2R50Y0",
|
||||
"zone_transfer_request_id":"f2ad17b5-807a-423f-a991-e06236c247be"
|
||||
}
|
||||
}
|
||||
|
||||
**Example Response**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"transfer_accept": {
|
||||
"id": "581891d5-99f5-49e1-86c3-eec0f44d66fd",
|
||||
"links": {
|
||||
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/581891d5-99f5-49e1-86c3-eec0f44d66fd",
|
||||
"zone": "http://127.0.0.1:9001/v2/zones/6b78734a-aef1-45cd-9708-8eb3c2d26ff8"
|
||||
},
|
||||
"status": "COMPLETE"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,10 @@
|
||||
"admin": "role:admin or is_admin:True",
|
||||
"owner": "tenant:%(tenant_id)s",
|
||||
"admin_or_owner": "rule:admin or rule:owner",
|
||||
"target": "tenant:%(target_tenant_id)s",
|
||||
"owner_or_target":"rule:target or rule:owner",
|
||||
"admin_or_owner_or_target":"rule:owner_or_target or rule:admin",
|
||||
"admin_or_target":"rule:admin or rule:target",
|
||||
|
||||
"default": "rule:admin_or_owner",
|
||||
|
||||
@ -76,5 +80,20 @@
|
||||
"diagnostics_ping": "rule:admin",
|
||||
"diagnostics_sync_domains": "rule:admin",
|
||||
"diagnostics_sync_domain": "rule:admin",
|
||||
"diagnostics_sync_record": "rule:admin"
|
||||
"diagnostics_sync_record": "rule:admin",
|
||||
|
||||
"create_zone_transfer_request": "rule:admin_or_owner",
|
||||
"get_zone_transfer_request": "rule:admin_or_owner or tenant:%(target_tenant_id)s or None:%(target_tenant_id)s",
|
||||
"get_zone_transfer_request_detailed": "rule:admin_or_owner",
|
||||
"find_zone_transfer_requests": "@",
|
||||
"find_zone_transfer_request": "@",
|
||||
"update_zone_transfer_request": "rule:admin_or_owner",
|
||||
"delete_zone_transfer_request": "rule:admin_or_owner",
|
||||
|
||||
"create_zone_transfer_accept": "rule:admin_or_owner or tenant:%(target_tenant_id)s or None:%(target_tenant_id)s",
|
||||
"get_zone_transfer_accept": "rule:admin_or_owner",
|
||||
"find_zone_transfer_accepts": "rule:admin",
|
||||
"find_zone_transfer_accept": "rule:admin",
|
||||
"update_zone_transfer_accept": "rule:admin",
|
||||
"delete_zone_transfer_accept": "rule:admin"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user