From 0025ea1df88c3ccb0b980682eeba80907e28942f Mon Sep 17 00:00:00 2001 From: Endre Karlson Date: Sat, 14 Mar 2015 18:36:10 +0100 Subject: [PATCH] Add code to allow triggering of AXFR from API Change-Id: I7a4232410f2bf4bb96d10f54a078202df7f436a6 --- .../v2/controllers/zones/tasks/__init__.py | 2 ++ .../api/v2/controllers/zones/tasks/xfr.py | 36 +++++++++++++++++++ designate/central/rpcapi.py | 10 ++++-- designate/central/service.py | 19 +++++++++- designate/tests/test_central/test_service.py | 18 ++++++++++ etc/designate/policy.json | 1 + 6 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 designate/api/v2/controllers/zones/tasks/xfr.py diff --git a/designate/api/v2/controllers/zones/tasks/__init__.py b/designate/api/v2/controllers/zones/tasks/__init__.py index 5db8b0a3..7153d377 100644 --- a/designate/api/v2/controllers/zones/tasks/__init__.py +++ b/designate/api/v2/controllers/zones/tasks/__init__.py @@ -21,6 +21,7 @@ from designate.api.v2.controllers.zones.tasks.transfer_requests \ from designate.api.v2.controllers.zones.tasks.transfer_accepts \ import TransferAcceptsController as TRA from designate.api.v2.controllers.zones.tasks import abandon +from designate.api.v2.controllers.zones.tasks.xfr import XfrController LOG = logging.getLogger(__name__) @@ -30,3 +31,4 @@ class TasksController(rest.RestController): transfer_accepts = TRA() transfer_requests = TRC() abandon = abandon.AbandonController() + xfr = XfrController() diff --git a/designate/api/v2/controllers/zones/tasks/xfr.py b/designate/api/v2/controllers/zones/tasks/xfr.py new file mode 100644 index 00000000..acbbc583 --- /dev/null +++ b/designate/api/v2/controllers/zones/tasks/xfr.py @@ -0,0 +1,36 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# +# Author: Endre Karlson +# +# 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 import utils +from designate.api.v2.controllers import rest + + +class XfrController(rest.RestController): + + @pecan.expose(template='json:', content_type='application/json') + @utils.validate_uuid('zone_id') + def post_all(self, zone_id): + """XFR a zone""" + request = pecan.request + response = pecan.response + context = request.environ['context'] + + self.central_api.perform_zone_xfr(context, zone_id) + response.status_int = 202 + + # NOTE: This is a hack and a half.. But Pecan needs it. + return '' diff --git a/designate/central/rpcapi.py b/designate/central/rpcapi.py index 13c202fc..2d620fb5 100644 --- a/designate/central/rpcapi.py +++ b/designate/central/rpcapi.py @@ -47,14 +47,15 @@ class CentralAPI(object): 4.2 - Add methods for pool manager integration 4.3 - Added Zone Transfer Methods 5.0 - Remove dead server code + 5.1 - Add xfr_domain """ - RPC_API_VERSION = '5.0' + RPC_API_VERSION = '5.1' 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='5.0') + self.client = rpc.get_client(target, version_cap='5.1') @classmethod def get_instance(cls): @@ -490,3 +491,8 @@ class CentralAPI(object): context, 'delete_zone_transfer_accept', zone_transfer_accept_id=zone_transfer_accept_id) + + def xfr_domain(self, context, domain_id): + LOG.info(_LI("xfr_domain: Calling central's xfr_domain")) + cctxt = self.client.prepare(version='5.1') + return cctxt.call(context, 'xfr_domain', domain_id=domain_id) diff --git a/designate/central/service.py b/designate/central/service.py index 39c79383..5358b6c1 100644 --- a/designate/central/service.py +++ b/designate/central/service.py @@ -247,7 +247,7 @@ def notification(notification_type): class Service(service.RPCService, service.Service): - RPC_API_VERSION = '5.0' + RPC_API_VERSION = '5.1' target = messaging.Target(version=RPC_API_VERSION) @@ -1014,6 +1014,23 @@ class Service(service.RPCService, service.Service): return domain + def xfr_domain(self, context, domain_id): + domain = self.storage.get_domain(context, domain_id) + + target = { + 'domain_id': domain_id, + 'domain_name': domain.name, + 'tenant_id': domain.tenant_id + } + + policy.check('xfr_domain', context, target) + + if domain.type == 'SECONDARY': + self.mdns_api.perform_zone_xfr(context, domain) + else: + msg = "Can't XFR a non Secondary zone." + raise exceptions.BadRequest(msg) + def count_domains(self, context, criterion=None): if criterion is None: criterion = {} diff --git a/designate/tests/test_central/test_service.py b/designate/tests/test_central/test_service.py index 20990050..dcc49c0f 100644 --- a/designate/tests/test_central/test_service.py +++ b/designate/tests/test_central/test_service.py @@ -20,6 +20,7 @@ import random import mock import testtools from testtools.matchers import GreaterThan +from oslo.config import cfg from oslo_log import log as logging from oslo_db import exception as db_exception from oslo_messaging.notify import notifier @@ -988,6 +989,23 @@ class CentralServiceTest(CentralTestCase): # Ensure the serial was incremented self.assertTrue(domain['serial'] > expected_domain['serial']) + def test_xfr_domain(self): + # Create a domain + fixture = self.get_domain_fixture('SECONDARY', 0) + fixture['email'] = cfg.CONF['service:central'].managed_resource_email + fixture['attributes'] = [{"key": "master", "value": "10.0.0.10"}] + + # Create a zone + secondary = self.create_domain(**fixture) + + self.central_service.xfr_domain(self.admin_context, secondary.id) + + def test_xfr_domain_invalid_type(self): + domain = self.create_domain() + + with testtools.ExpectedException(exceptions.BadRequest): + self.central_service.xfr_domain(self.admin_context, domain.id) + # RecordSet Tests def test_create_recordset(self): domain = self.create_domain() diff --git a/etc/designate/policy.json b/etc/designate/policy.json index cdc401cd..2642f523 100644 --- a/etc/designate/policy.json +++ b/etc/designate/policy.json @@ -46,6 +46,7 @@ "find_domain": "rule:admin_or_owner", "update_domain": "rule:admin_or_owner", "delete_domain": "rule:admin_or_owner", + "xfr_domain": "rule:admin_or_owner", "abandon_domain": "rule:admin", "count_domains": "rule:admin_or_owner", "touch_domain": "rule:admin_or_owner",