Beispiel #1
0
class SchedulerClientTestCase(test.TestCase):
    def setUp(self):
        super(SchedulerClientTestCase, self).setUp()
        self.client = scheduler_client.SchedulerClient()

    def test_constructor(self):
        self.assertIsNotNone(self.client.queryclient)
        self.assertIsNotNone(self.client.reportclient)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'select_destinations')
    def test_select_destinations(self, mock_select_destinations):
        self.assertIsNone(self.client.queryclient.instance)

        self.client.select_destinations('ctxt', 'fake_spec', 'fake_prop')

        self.assertIsNotNone(self.client.queryclient.instance)
        mock_select_destinations.assert_called_once_with(
            'ctxt', 'fake_spec', 'fake_prop')

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'select_destinations',
                       side_effect=messaging.MessagingTimeout())
    def test_select_destinations_timeout(self, mock_select_destinations):
        # check if the scheduler service times out properly
        fake_args = ['ctxt', 'fake_spec', 'fake_prop']
        self.assertRaises(messaging.MessagingTimeout,
                          self.client.select_destinations, *fake_args)
        mock_select_destinations.assert_has_calls([mock.call(*fake_args)] * 2)

    @mock.patch.object(
        scheduler_query_client.SchedulerQueryClient,
        'select_destinations',
        side_effect=[messaging.MessagingTimeout(), mock.DEFAULT])
    def test_select_destinations_timeout_once(self, mock_select_destinations):
        # scenario: the scheduler service times out & recovers after failure
        fake_args = ['ctxt', 'fake_spec', 'fake_prop']
        self.client.select_destinations(*fake_args)
        mock_select_destinations.assert_has_calls([mock.call(*fake_args)] * 2)

    @mock.patch.object(scheduler_report_client.SchedulerReportClient,
                       'update_resource_stats')
    def test_update_resource_stats(self, mock_update_resource_stats):
        self.assertIsNone(self.client.reportclient.instance)

        self.client.update_resource_stats('ctxt', 'fake_name', 'fake_stats')

        self.assertIsNotNone(self.client.reportclient.instance)
        mock_update_resource_stats.assert_called_once_with(
            'ctxt', 'fake_name', 'fake_stats')
Beispiel #2
0
        def _get_single_host():
            hosts = self.get_hosts(target)
            try:
                if not hosts:
                    err_msg = "No hosts were found for target %s." % target
                    LOG.error(err_msg)
                    raise oslo_messaging.InvalidTarget(err_msg, target)

                if len(hosts) == 1:
                    host = hosts[0]
                    LOG.info(_LI(
                        "A single host %(host)s found for target %(target)s.")
                        % {"host": host, "target": target})
                else:
                    host = random.choice(hosts)
                    LOG.warning(_LW(
                        "Multiple hosts %(hosts)s were found for target "
                        " %(target)s. Using the random one - %(host)s.")
                        % {"hosts": hosts, "target": target, "host": host})
                return host
            except oslo_messaging.InvalidTarget as ex:
                if timeout:
                    raise oslo_messaging.MessagingTimeout()
                else:
                    raise ex
    def _send(self, target, ctxt, message, wait_for_reply=None, timeout=None):
        self._check_serialize(message)

        exchange = self._exchange_manager.get_exchange(target.exchange)

        reply_q = None
        if wait_for_reply:
            reply_q = moves.queue.Queue()

        exchange.deliver_message(target.topic, ctxt, message,
                                 server=target.server,
                                 fanout=target.fanout,
                                 reply_q=reply_q)

        if wait_for_reply:
            try:
                reply, failure = reply_q.get(timeout=timeout)
                if failure:
                    raise failure
                else:
                    return reply
            except moves.queue.Empty:
                raise oslo_messaging.MessagingTimeout(
                    'No reply on topic %s' % target.topic)

        return None
Beispiel #4
0
 def get(self, msg_id, timeout):
     try:
         return self._queues[msg_id].get(block=True, timeout=timeout)
     except moves.queue.Empty:
         raise oslo_messaging.MessagingTimeout(
             'Timed out waiting for a reply '
             'to message ID %s' % msg_id)
Beispiel #5
0
 def test_get_status_not_ok_timeout(self):
     with mock.patch('pecan.request') as request:
         client = mock.Mock()
         client.call.side_effect = oslo_messaging.MessagingTimeout()
         request.client.prepare.return_value = client
         resp = self.get_json('/status/', expect_errors=True)
         self.assertEqual(503, resp.status_code)
         self.assertIn('vitrage-graph is not available', resp.text)
Beispiel #6
0
 def get_reply(self, timeout):
     """Retrieve the reply."""
     if not self._reply_queue:
         return None
     try:
         return self._reply_queue.get(timeout=timeout)
     except moves.queue.Empty:
         raise oslo_messaging.MessagingTimeout(
             'Timed out waiting for a reply')
Beispiel #7
0
 def test_engine_alive_timeout(self):
     slock = stack_lock.StackLock(self.context, self.stack, self.engine_id)
     mget_client = self.patchobject(stack_lock.rpc_messaging,
                                    'get_rpc_client')
     mclient = mget_client.return_value
     mclient_ctx = mclient.prepare.return_value
     mclient_ctx.call.side_effect = messaging.MessagingTimeout('too slow')
     ret = slock.engine_alive(self.context, self.engine_id)
     self.assertIs(False, ret)
     mclient.prepare.assert_called_once_with(timeout=2)
     mclient_ctx.call.assert_called_once_with(self.context, 'listening')
Beispiel #8
0
    def test_conductor_alive_timeout(self, mock_listener_api_new):
        mock_listener_api = mock.MagicMock()
        mock_listener_api.ping_conductor.side_effect = (
            messaging.MessagingTimeout('too slow'))
        mock_listener_api_new.return_value = mock_listener_api
        baylock = bay_lock.BayLock(self.context, self.bay, self.conductor_id)

        ret = baylock.conductor_alive(self.context, self.conductor_id)

        self.assertIs(False, ret)
        self.assertEqual(1, mock_listener_api_new.call_count)
Beispiel #9
0
    def send_request(self, request):
        reply_future = self.sender.send_request(request)
        try:
            reply = reply_future.result(timeout=request.timeout)
        except futures.TimeoutError:
            raise oslo_messaging.MessagingTimeout(
                "Timeout %s seconds was reached" % request.timeout)
        finally:
            self.reply_waiter.untrack_id(request.message_id)

        LOG.debug("Received reply %s", reply)
        if reply[zmq_names.FIELD_FAILURE]:
            raise rpc_common.deserialize_remote_exception(
                reply[zmq_names.FIELD_FAILURE], request.allowed_remote_exmods)
        else:
            return reply[zmq_names.FIELD_REPLY]
    def _receive_reply(socket, request):
        def _receive_method(socket):
            return socket.recv_pyobj()

        # NOTE(ozamiatin): Check for retry here (no retries now)
        with contextlib.closing(zmq_async.get_reply_poller()) as poller:
            poller.register(socket, recv_method=_receive_method)
            reply, socket = poller.poll(timeout=request.timeout)
            if reply is None:
                raise oslo_messaging.MessagingTimeout(
                    "Timeout %s seconds was reached" % request.timeout)
            if reply[zmq_names.FIELD_FAILURE]:
                raise rpc_common.deserialize_remote_exception(
                    reply[zmq_names.FIELD_FAILURE],
                    request.allowed_remote_exmods)
            else:
                return reply[zmq_names.FIELD_REPLY]
 def test_get_dvr_mac_address_retried(self):
     valid_entry = {'host': 'cn1', 'mac_address': 'aa:22:33:44:55:66'}
     raise_timeout = oslo_messaging.MessagingTimeout()
     # Raise a timeout the first 2 times it calls get_dvr_mac_address()
     self._setup_for_dvr_test()
     self.agent.dvr_agent.dvr_mac_address = None
     with mock.patch.object(self.agent.dvr_agent.plugin_rpc,
                            'get_dvr_mac_address_by_host',
                            side_effect=(raise_timeout, raise_timeout,
                                         valid_entry)):
         self.agent.dvr_agent.get_dvr_mac_address()
         self.assertEqual('aa:22:33:44:55:66',
                          self.agent.dvr_agent.dvr_mac_address)
         self.assertTrue(self.agent.dvr_agent.in_distributed_mode())
         self.assertEqual(
             self.agent.dvr_agent.plugin_rpc.get_dvr_mac_address_by_host.
             call_count, 3)
 def test_get_dvr_mac_address_retried_max(self):
     raise_timeout = oslo_messaging.MessagingTimeout()
     # Raise a timeout every time until we give up, currently 5 tries
     self._setup_for_dvr_test()
     self.agent.dvr_agent.dvr_mac_address = None
     with contextlib.nested(
             mock.patch.object(self.agent.dvr_agent.plugin_rpc,
                               'get_dvr_mac_address_by_host',
                               side_effect=raise_timeout),
             mock.patch.object(utils, "execute"),
     ) as (rpc_mock, execute_mock):
         self.agent.dvr_agent.get_dvr_mac_address()
         self.assertIsNone(self.agent.dvr_agent.dvr_mac_address)
         self.assertFalse(self.agent.dvr_agent.in_distributed_mode())
         self.assertEqual(
             self.agent.dvr_agent.plugin_rpc.get_dvr_mac_address_by_host.
             call_count, 5)
Beispiel #13
0
    def test_engine_alive_timeout(self, rpc_client_method):
        mock_rpc_client = rpc_client_method.return_value
        mock_prepare_method = mock_rpc_client.prepare
        mock_prepare_client = mock_prepare_method.return_value
        mock_cnxt = mock.Mock()

        listener_client = rpc_client.EngineListenerClient('engine-007')
        rpc_client_method.assert_called_once_with(
            version=rpc_client.EngineListenerClient.BASE_RPC_API_VERSION,
            topic=rpc_api.LISTENER_TOPIC,
            server='engine-007',
        )
        mock_prepare_method.assert_called_once_with(timeout=2)
        self.assertEqual(mock_prepare_client, listener_client._client,
                         "Failed to create RPC client")

        mock_prepare_client.call.side_effect = messaging.MessagingTimeout(
            'too slow')
        ret = listener_client.is_alive(mock_cnxt)
        self.assertFalse(ret)
        mock_prepare_client.call.assert_called_once_with(
            mock_cnxt, 'listening')
    def send_request(self, request):
        reply_future = self.sender.send_request(request)
        try:
            reply = reply_future.result(timeout=request.timeout)
            LOG.debug("Received reply %s", request.message_id)
        except AssertionError:
            LOG.error(_LE("Message format error in reply %s"),
                      request.message_id)
            return None
        except futures.TimeoutError:
            raise oslo_messaging.MessagingTimeout(
                "Timeout %(tout)s seconds was reached for message %(id)s" % {
                    "tout": request.timeout,
                    "id": request.message_id
                })
        finally:
            self.reply_waiter.untrack_id(request.message_id)

        if reply.failure:
            raise rpc_common.deserialize_remote_exception(
                reply.failure, request.allowed_remote_exmods)
        else:
            return reply.reply_body
Beispiel #15
0
 def _raise_timeout_exception(msg_id):
     raise oslo_messaging.MessagingTimeout(
         _('Timed out waiting for a reply to message ID %s.') % msg_id)
Beispiel #16
0
class SchedulerClientTestCase(test.NoDBTestCase):

    def setUp(self):
        super(SchedulerClientTestCase, self).setUp()
        self.client = scheduler_client.SchedulerClient()

    def test_constructor(self):
        self.assertIsNotNone(self.client.queryclient)
        self.assertIsNotNone(self.client.reportclient)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'select_destinations')
    def test_select_destinations(self, mock_select_destinations):
        fake_spec = objects.RequestSpec()
        fake_spec.instance_uuid = uuids.instance

        self.client.select_destinations('ctxt', fake_spec,
                [fake_spec.instance_uuid])

        mock_select_destinations.assert_called_once_with('ctxt', fake_spec,
                [fake_spec.instance_uuid], False, False)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'select_destinations',
                       side_effect=messaging.MessagingTimeout())
    def test_select_destinations_timeout(self, mock_select_destinations):
        # check if the scheduler service times out properly
        fake_spec = objects.RequestSpec()
        fake_spec.instance_uuid = uuids.instance
        fake_args = ['ctxt', fake_spec, [fake_spec.instance_uuid], False,
                False]
        self.assertRaises(messaging.MessagingTimeout,
                          self.client.select_destinations, *fake_args)
        mock_select_destinations.assert_has_calls([mock.call(*fake_args)] * 2)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'select_destinations', side_effect=[
                           messaging.MessagingTimeout(), mock.DEFAULT])
    def test_select_destinations_timeout_once(self, mock_select_destinations):
        # scenario: the scheduler service times out & recovers after failure
        fake_spec = objects.RequestSpec()
        fake_spec.instance_uuid = uuids.instance
        fake_args = ['ctxt', fake_spec, [fake_spec.instance_uuid], False,
                False]
        self.client.select_destinations(*fake_args)
        mock_select_destinations.assert_has_calls([mock.call(*fake_args)] * 2)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'update_aggregates')
    def test_update_aggregates(self, mock_update_aggs):
        aggregates = [objects.Aggregate(id=1)]
        self.client.update_aggregates(
            context='context',
            aggregates=aggregates)
        mock_update_aggs.assert_called_once_with(
            'context', aggregates)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'delete_aggregate')
    def test_delete_aggregate(self, mock_delete_agg):
        aggregate = objects.Aggregate(id=1)
        self.client.delete_aggregate(
            context='context',
            aggregate=aggregate)
        mock_delete_agg.assert_called_once_with(
            'context', aggregate)

    @mock.patch.object(scheduler_report_client.SchedulerReportClient,
                       'update_compute_node')
    def test_update_compute_node(self, mock_update_compute_node):
        self.client.update_compute_node(mock.sentinel.ctx, mock.sentinel.cn)

        mock_update_compute_node.assert_called_once_with(
            mock.sentinel.ctx, mock.sentinel.cn)

    @mock.patch.object(scheduler_report_client.SchedulerReportClient,
                       'set_inventory_for_provider')
    def test_set_inventory_for_provider(self, mock_set):
        self.client.set_inventory_for_provider(
            mock.sentinel.ctx,
            mock.sentinel.rp_uuid,
            mock.sentinel.rp_name,
            mock.sentinel.inv_data,
        )
        mock_set.assert_called_once_with(
            mock.sentinel.ctx,
            mock.sentinel.rp_uuid,
            mock.sentinel.rp_name,
            mock.sentinel.inv_data,
            parent_provider_uuid=None,
        )
        # Pass the optional parent_provider_uuid
        mock_set.reset_mock()
        self.client.set_inventory_for_provider(
            mock.sentinel.ctx,
            mock.sentinel.child_uuid,
            mock.sentinel.child_name,
            mock.sentinel.inv_data2,
            parent_provider_uuid=mock.sentinel.rp_uuid,
        )
        mock_set.assert_called_once_with(
            mock.sentinel.ctx,
            mock.sentinel.child_uuid,
            mock.sentinel.child_name,
            mock.sentinel.inv_data2,
            parent_provider_uuid=mock.sentinel.rp_uuid,
        )
Beispiel #17
0
class ApiV2ZonesTest(ApiV2TestCase):
    def setUp(self):
        super(ApiV2ZonesTest, self).setUp()

        # Create the default TLDs
        self.create_default_tlds()

    def test_create_zone(self):
        # Create a zone
        fixture = self.get_domain_fixture(fixture=0)

        response = self.client.post_json('/zones/', fixture)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertEqual('PENDING', response.json['status'])
        self.assertEqual('PRIMARY', response.json['type'])
        self.assertEqual([], response.json['masters'])
        self.assertIsNone(response.json['updated_at'])

        for k in fixture:
            self.assertEqual(fixture[k], response.json[k])

    def test_create_zone_no_type(self):
        # Create a zone
        fixture = self.get_domain_fixture(fixture=0)
        del fixture['type']

        response = self.client.post_json('/zones/', fixture)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertEqual('PENDING', response.json['status'])
        self.assertEqual('PRIMARY', response.json['type'])
        self.assertEqual([], response.json['masters'])
        self.assertIsNone(response.json['updated_at'])

        for k in fixture:
            self.assertEqual(fixture[k], response.json[k])

    def test_create_zone_validation(self):
        # NOTE: The schemas should be tested separately to the API. So we
        #       don't need to test every variation via the API itself.
        # Fetch a fixture
        fixture = self.get_domain_fixture(fixture=0)

        # Add a junk field to the body
        fixture['junk'] = 'Junk Field'

        # Ensure it fails with a 400
        body = fixture

        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_body_validation(self):
        fixture = self.get_domain_fixture(fixture=0)
        # Add id to the body
        fixture['id'] = '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
        # Ensure it fails with a 400
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

        fixture = self.get_domain_fixture(fixture=0)
        # Add created_at to the body
        fixture['created_at'] = '2014-03-12T19:07:53.000000'
        # Ensure it fails with a 400
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_invalid_name(self):
        # Try to create a zone with an invalid name
        fixture = self.get_domain_fixture(fixture=-1)

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', fixture)

    @patch.object(central_service.Service,
                  'create_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_create_zone_timeout(self, _):
        fixture = self.get_domain_fixture(fixture=0)

        body = fixture

        self._assert_exception('timeout', 504, self.client.post_json,
                               '/zones/', body)

    @patch.object(central_service.Service,
                  'create_domain',
                  side_effect=exceptions.DuplicateDomain())
    def test_create_zone_duplicate(self, _):
        fixture = self.get_domain_fixture(fixture=0)

        body = fixture

        self._assert_exception('duplicate_domain', 409, self.client.post_json,
                               '/zones/', body)

    def test_create_zone_missing_content_type(self):
        self._assert_exception('unsupported_content_type', 415,
                               self.client.post, '/zones')

    def test_create_zone_bad_content_type(self):
        self._assert_exception('unsupported_content_type',
                               415,
                               self.client.post,
                               '/zones',
                               headers={'Content-type': 'test/goat'})

    def test_zone_invalid_url(self):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980/invalid'
        self._assert_exception('not_found',
                               404,
                               self.client.get,
                               url,
                               headers={'Accept': 'application/json'})
        self._assert_exception('not_found', 404, self.client.patch_json, url)
        self._assert_exception('not_found', 404, self.client.delete, url)

        # Pecan returns a 405 for post
        response = self.client.post(url, status=405)
        self.assertEqual(405, response.status_int)

    def test_get_zones(self):
        response = self.client.get('/zones/')

        # 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('zones', response.json)
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # We should start with 0 zones
        self.assertEqual(0, len(response.json['zones']))

        # We should start with 0 zones
        self.assertEqual(0, len(response.json['zones']))

        data = [self.create_domain(name='x-%s.com.' % i) for i in 'abcdefghij']
        self._assert_paging(data, '/zones', key='zones')

        self._assert_invalid_paging(data, '/zones', key='zones')

    @patch.object(central_service.Service,
                  'find_domains',
                  side_effect=messaging.MessagingTimeout())
    def test_get_zones_timeout(self, _):
        self._assert_exception('timeout', 504, self.client.get, '/zones/')

    def test_get_zone(self):
        # Create a zone
        zone = self.create_domain()

        response = self.client.get('/zones/%s' % zone['id'],
                                   headers=[('Accept', 'application/json')])

        # 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('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertEqual('PENDING', response.json['status'])
        self.assertIsNone(response.json['updated_at'])
        self.assertEqual(zone['name'], response.json['name'])
        self.assertEqual(zone['email'], response.json['email'])

    def test_get_zone_invalid_id(self):
        self._assert_invalid_uuid(self.client.get, '/zones/%s')

    @patch.object(central_service.Service,
                  'get_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_get_zone_timeout(self, _):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
        self._assert_exception('timeout',
                               504,
                               self.client.get,
                               url,
                               headers={'Accept': 'application/json'})

    @patch.object(central_service.Service,
                  'get_domain',
                  side_effect=exceptions.DomainNotFound())
    def test_get_zone_missing(self, _):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
        self._assert_exception('domain_not_found',
                               404,
                               self.client.get,
                               url,
                               headers={'Accept': 'application/json'})

    def test_get_zone_bad_accept(self):
        url = '/zones/6e2146f3-87bc-4f47-adc5-4df0a5c78218'

        self.client.get(url, headers={'Accept': 'test/goat'}, status=406)

    def test_update_zone(self):
        # Create a zone
        zone = self.create_domain()

        # Prepare an update body
        body = {'email': 'prefix-%s' % zone['email']}

        response = self.client.patch_json('/zones/%s' % zone['id'],
                                          body,
                                          status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])
        self.assertIn('status', response.json)

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIsNotNone(response.json['updated_at'])
        self.assertEqual('prefix-%s' % zone['email'], response.json['email'])

    def test_update_zone_invalid_id(self):
        self._assert_invalid_uuid(self.client.patch_json, '/zones/%s')

    def test_update_zone_validation(self):
        # NOTE: The schemas should be tested separatly to the API. So we
        #       don't need to test every variation via the API itself.
        # Create a zone
        zone = self.create_domain()

        # Prepare an update body with junk in the body
        body = {'email': 'prefix-%s' % zone['email'], 'junk': 'Junk Field'}

        url = '/zones/%s' % zone['id']

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.patch_json,
                               url, body)

        # Prepare an update body with negative ttl in the body
        body = {'email': 'prefix-%s' % zone['email'], 'ttl': -20}

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.patch_json,
                               url, body)

        # Prepare an update body with ttl > maximum (2147483647) in the body
        body = {'email': 'prefix-%s' % zone['email'], 'ttl': 2147483648}

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.patch_json,
                               url, body)

    @patch.object(central_service.Service,
                  'get_domain',
                  side_effect=exceptions.DuplicateDomain())
    def test_update_zone_duplicate(self, _):
        # Prepare an update body
        body = {'email': '*****@*****.**'}

        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 409
        self._assert_exception('duplicate_domain', 409, self.client.patch_json,
                               url, body)

    @patch.object(central_service.Service,
                  'get_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_update_zone_timeout(self, _):
        # Prepare an update body
        body = {'email': '*****@*****.**'}

        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 504
        self._assert_exception('timeout', 504, self.client.patch_json, url,
                               body)

    @patch.object(central_service.Service,
                  'get_domain',
                  side_effect=exceptions.DomainNotFound())
    def test_update_zone_missing(self, _):
        # Prepare an update body
        body = {'email': '*****@*****.**'}

        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 404
        self._assert_exception('domain_not_found', 404, self.client.patch_json,
                               url, body)

    def test_delete_zone(self):
        zone = self.create_domain()

        response = self.client.delete('/zones/%s' % zone['id'], status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual('DELETE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

    def test_delete_zone_invalid_id(self):
        self._assert_invalid_uuid(self.client.delete, '/zones/%s')

    @patch.object(central_service.Service,
                  'delete_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_delete_zone_timeout(self, _):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        self._assert_exception('timeout', 504, self.client.delete, url)

    @patch.object(central_service.Service,
                  'delete_domain',
                  side_effect=exceptions.DomainNotFound())
    def test_delete_zone_missing(self, _):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        self._assert_exception('domain_not_found', 404, self.client.delete,
                               url)

    def test_post_abandon_zone(self):
        zone = self.create_domain()
        url = '/zones/%s/tasks/abandon' % zone.id

        # Ensure that we get permission denied
        self._assert_exception('forbidden', 403, self.client.post_json, url)

        # Ensure that abandon zone succeeds with the right policy
        self.policy({'abandon_domain': '@'})
        response = self.client.post_json(url)
        self.assertEqual(204, response.status_int)

    def test_get_abandon_zone(self):
        zone = self.create_domain()
        url = '/zones/%s/tasks/abandon' % zone.id
        self._assert_exception('method_not_allowed', 405, self.client.get, url)

    def test_get_invalid_abandon(self):
        # This is an invalid endpoint - should return 404
        url = '/zones/tasks/abandon'
        self._assert_exception('not_found', 404, self.client.get, url)

    def test_get_zone_tasks(self):
        # This is an invalid endpoint - should return 404
        zone = self.create_domain()
        url = '/zones/%s/tasks' % zone.id
        self._assert_exception('not_found', 404, self.client.get, url)

    def test_create_secondary(self):
        # Create a zone
        fixture = self.get_domain_fixture('SECONDARY', 0)
        fixture['masters'] = ["10.0.0.1"]

        response = self.client.post_json('/zones/', fixture)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertEqual('PENDING', response.json['status'])
        self.assertEqual(cfg.CONF['service:central'].managed_resource_email,
                         response.json['email'])

        self.assertIsNone(response.json['updated_at'])
        # Zone is not transferred yet
        self.assertIsNone(response.json['transferred_at'])
        # Serial defaults to 1
        self.assertEqual(response.json['serial'], 1)

        for k in fixture:
            self.assertEqual(fixture[k], response.json[k])

    def test_create_secondary_no_masters(self):
        # Create a zone
        fixture = self.get_domain_fixture('SECONDARY', 0)

        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones/', fixture)

    def test_update_secondary(self):
        # Create a zone
        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
        zone = self.create_domain(**fixture)

        masters = ['10.0.0.1', '10.0.0.2']

        # Prepare an update body
        body = {'masters': masters}

        response = self.client.patch_json('/zones/%s' % zone['id'],
                                          body,
                                          status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])
        self.assertIn('status', response.json)

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIsNotNone(response.json['updated_at'])
        self.assertEqual(masters, response.json['masters'])
        self.assertEqual(1, response.json['serial'])

    def test_xfr_request(self):
        # Create a zone
        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
        zone = self.create_domain(**fixture)

        response = self.client.post_json('/zones/%s/tasks/xfr' % zone['id'],
                                         None,
                                         status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

    def test_invalid_xfr_request(self):
        # Create a zone

        # Create a zone
        zone = self.create_domain()

        response = self.client.post_json('/zones/%s/tasks/xfr' % zone['id'],
                                         None,
                                         status=400)

        # Check the headers are what we expect
        self.assertEqual(400, response.status_int)
        self.assertEqual('application/json', response.content_type)

    def test_update_secondary_email_invalid_object(self):
        # Create a zone
        fixture = self.get_domain_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        # Create a zone
        zone = self.create_domain(**fixture)

        body = {'email': '*****@*****.**'}

        self._assert_exception('invalid_object', 400, self.client.patch_json,
                               '/zones/%s' % zone['id'], body)

    # Metadata tests
    def test_metadata_exists(self):
        response = self.client.get('/zones/')

        # Make sure the fields exist
        self.assertIn('metadata', response.json)
        self.assertIn('total_count', response.json['metadata'])

    def test_total_count(self):
        response = self.client.get('/zones/')

        # There are no zones by default
        self.assertEqual(0, response.json['metadata']['total_count'])

        # Create a zone
        fixture = self.get_domain_fixture(fixture=0)
        response = self.client.post_json('/zones/', fixture)

        response = self.client.get('/zones/')

        # Make sure total_count picked it up
        self.assertEqual(1, response.json['metadata']['total_count'])

    def test_total_count_pagination(self):
        # Create two zones
        fixture = self.get_domain_fixture(fixture=0)
        response = self.client.post_json('/zones/', fixture)

        fixture = self.get_domain_fixture(fixture=1)
        response = self.client.post_json('/zones/', fixture)

        # Paginate so that there is only one zone returned
        response = self.client.get('/zones?limit=1')

        self.assertEqual(1, len(response.json['zones']))

        # The total_count should know there are two
        self.assertEqual(2, response.json['metadata']['total_count'])

    def test_no_update_deleting(self):
        # Create a zone
        zone = self.create_domain()

        # Prepare an update body
        body = {'zone': {'email': 'prefix-%s' % zone['email']}}

        self.client.delete('/zones/%s' % zone['id'], status=202)
        self._assert_exception('bad_request', 400, self.client.patch_json,
                               '/zones/%s' % zone['id'], body)

    def test_get_nameservers(self):
        # Create a zone
        zone = self.create_domain()

        # Prepare an update body

        response = self.client.get('/zones/%s/nameservers' % zone['id'],
                                   headers=[('Accept', 'application/json')])

        self.assertIn('nameservers', response.json)
        self.assertEqual(1, len(response.json['nameservers']))
        self.assertIn('hostname', response.json['nameservers'][0])
        self.assertIn('priority', response.json['nameservers'][0])
 def _raise_timeout(request):
     raise oslo_messaging.MessagingTimeout(
         "Timeout %(tout)s seconds was reached for message %(msg_id)s" % {
             "tout": request.timeout,
             "msg_id": request.message_id
         })
Beispiel #19
0
class ApiV2TsigKeysTest(ApiV2TestCase):
    def setUp(self):
        super(ApiV2TsigKeysTest, self).setUp()

        # Set the policy to accept everyone as an admin, as this is an
        # admin-only API
        self.policy({'admin': '@'})

    def test_create_tsigkey(self):
        # Create a TSIG Key
        fixture = self.get_tsigkey_fixture(0)
        response = self.client.post_json('/tsigkeys/', fixture)

        # 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('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the generated values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertIsNone(response.json['updated_at'])

        # Check the supplied values returned are what we expect
        self.assertDictContainsSubset(fixture, response.json)

    def test_create_tsigkey_validation(self):
        # NOTE: The schemas should be tested separately to the API. So we
        #       don't need to test every variation via the API itself.
        # Fetch a fixture
        fixture = self.get_tsigkey_fixture(0)

        # Add a junk field to the body
        fixture['junk'] = 'Junk Field'

        # Ensure it fails with a 400
        body = fixture

        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/tsigkeys', body)

    def test_create_tsigkey_duplicate(self):
        # Prepare a TSIG Key fixture
        fixture = self.get_tsigkey_fixture(0)
        body = fixture

        # Create the first TSIG Key
        response = self.client.post_json('/tsigkeys', body)
        self.assertEqual(201, response.status_int)

        self._assert_exception('duplicate_tsigkey', 409,
                               self.client.post_json, '/tsigkeys', body)

    def test_get_tsigkeys(self):
        response = self.client.get('/tsigkeys/')

        # 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('tsigkeys', response.json)
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # We should start with 0 tsigkeys
        self.assertEqual(0, len(response.json['tsigkeys']))

        data = [self.create_tsigkey(name='tsigkey-%s' % i)
                for i in range(1, 10)]
        self._assert_paging(data, '/tsigkeys', key='tsigkeys')
        self._assert_invalid_paging(data, '/tsigkeys', key='tsigkeys')

    @patch.object(central_service.Service, 'find_tsigkeys',
                  side_effect=messaging.MessagingTimeout())
    def test_get_tsigkeys_timeout(self, _):
        self._assert_exception('timeout', 504, self.client.get, '/tsigkeys/')

    def test_get_tsigkey(self):
        # Create a tsigkey
        tsigkey = self.create_tsigkey()

        response = self.client.get('/tsigkeys/%s' % tsigkey.id,
                                   headers=[('Accept', 'application/json')])

        # 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('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the generated values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertIsNone(response.json['updated_at'])

        # Check the supplied values returned are what we expect
        self.assertEqual(tsigkey.name, response.json['name'])
        self.assertEqual(
            tsigkey.algorithm, response.json['algorithm'])
        self.assertEqual(tsigkey.secret, response.json['secret'])
        self.assertEqual(tsigkey.scope, response.json['scope'])
        self.assertEqual(
            tsigkey.resource_id, response.json['resource_id'])

    def test_get_tsigkey_invalid_id(self):
        self._assert_invalid_uuid(self.client.get, '/tsigkeys/%s')

    @patch.object(central_service.Service, 'get_tsigkey',
                  side_effect=messaging.MessagingTimeout())
    def test_get_tsigkey_timeout(self, _):
        url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
        self._assert_exception('timeout', 504, self.client.get, url,
                               headers={'Accept': 'application/json'})

    @patch.object(central_service.Service, 'get_tsigkey',
                  side_effect=exceptions.TsigKeyNotFound())
    def test_get_tsigkey_missing(self, _):
        url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
        self._assert_exception('tsigkey_not_found', 404, self.client.get, url,
                               headers={'Accept': 'application/json'})

    def test_update_tsigkey(self):
        # Create a TSIG Key
        tsigkey = self.create_tsigkey()

        # Prepare an update body
        body = {'secret': 'prefix-%s' % tsigkey.secret}

        response = self.client.patch_json('/tsigkeys/%s' % tsigkey.id, body)

        # 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('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIsNotNone(response.json['updated_at'])
        self.assertEqual('prefix-%s' % tsigkey['secret'],
                         response.json['secret'])

    def test_update_tsigkey_invalid_id(self):
        self._assert_invalid_uuid(self.client.patch_json, '/tsigkeys/%s')

    @patch.object(central_service.Service, 'get_tsigkey',
                  side_effect=exceptions.DuplicateTsigKey())
    def test_update_tsigkey_duplicate(self, _):
        # Prepare an update body
        body = {'name': 'AnyOldName'}

        url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 409
        self._assert_exception('duplicate_tsigkey', 409,
                               self.client.patch_json, url, body)

    @patch.object(central_service.Service, 'get_tsigkey',
                  side_effect=messaging.MessagingTimeout())
    def test_update_tsigkey_timeout(self, _):
        # Prepare an update body
        body = {'name': 'AnyOldName'}

        url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 504
        self._assert_exception('timeout', 504, self.client.patch_json,
                               url, body)

    @patch.object(central_service.Service, 'get_tsigkey',
                  side_effect=exceptions.TsigKeyNotFound())
    def test_update_tsigkey_missing(self, _):
        # Prepare an update body
        body = {'name': 'AnyOldName'}

        url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 404
        self._assert_exception('tsigkey_not_found', 404,
                               self.client.patch_json, url, body)

    def test_delete_tsigkey(self):
        tsigkey = self.create_tsigkey()

        self.client.delete('/tsigkeys/%s' % tsigkey['id'], status=204)

    def test_delete_tsigkey_invalid_id(self):
        self._assert_invalid_uuid(self.client.delete, '/tsigkeys/%s')

    @patch.object(central_service.Service, 'delete_tsigkey',
                  side_effect=messaging.MessagingTimeout())
    def test_delete_tsigkey_timeout(self, _):
        url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        self._assert_exception('timeout', 504, self.client.delete, url)

    @patch.object(central_service.Service, 'delete_tsigkey',
                  side_effect=exceptions.TsigKeyNotFound())
    def test_delete_tsigkey_missing(self, _):
        url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        self._assert_exception('tsigkey_not_found', 404, self.client.delete,
                               url)
Beispiel #20
0
class ApiV1DomainsTest(ApiV1Test):
    def test_create_domain(self):
        # Create a domain
        fixture = self.get_domain_fixture(0)

        # V1 doesn't have these
        del fixture['type']

        response = self.post('domains', data=fixture)

        self.assertIn('id', response.json)
        self.assertIn('name', response.json)
        self.assertEqual(response.json['name'], fixture['name'])

    def test_create_domain_junk(self):
        # Create a domain
        fixture = self.get_domain_fixture(0)

        # Add a junk property
        fixture['junk'] = 'Junk Field'

        # Ensure it fails with a 400
        self.post('domains', data=fixture, status_code=400)

    @patch.object(central_service.Service,
                  'create_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_create_domain_timeout(self, _):
        # Create a domain
        fixture = self.get_domain_fixture(0)

        # V1 doesn't have these
        del fixture['type']

        self.post('domains', data=fixture, status_code=504)

    @patch.object(central_service.Service,
                  'create_domain',
                  side_effect=exceptions.DuplicateDomain())
    def test_create_domain_duplicate(self, _):
        # Create a domain
        fixture = self.get_domain_fixture(0)

        # V1 doesn't have these
        del fixture['type']

        self.post('domains', data=fixture, status_code=409)

    def test_create_domain_null_ttl(self):
        # Create a domain
        fixture = self.get_domain_fixture(0)
        fixture['ttl'] = None
        self.post('domains', data=fixture, status_code=400)

    def test_create_domain_negative_ttl(self):
        # Create a domain
        fixture = self.get_domain_fixture(0)
        fixture['ttl'] = -1
        self.post('domains', data=fixture, status_code=400)

    def test_create_domain_invalid_ttl(self):
        # Create a domain
        fixture = self.get_domain_fixture(0)
        fixture['ttl'] = "$?>&"
        self.post('domains', data=fixture, status_code=400)

    def test_create_domain_utf_description(self):
        # Create a domain
        fixture = self.get_domain_fixture(0)

        # V1 doesn't have type
        del fixture['type']

        # Give it a UTF-8 filled description
        fixture['description'] = "utf-8:2H₂+O₂⇌2H₂O,R=4.7kΩ,⌀200mm∮E⋅da=Q,n" \
                                 ",∑f(i)=∏g(i),∀x∈ℝ:⌈x⌉"
        # Create the domain, ensuring it succeeds, thus UTF-8 is supported
        self.post('domains', data=fixture)

    def test_create_domain_description_too_long(self):
        # Create a domain
        fixture = self.get_domain_fixture(0)
        fixture['description'] = "x" * 161

        # Create the domain, ensuring it fails with a 400
        self.post('domains', data=fixture, status_code=400)

    def test_create_domain_with_unwanted_attributes(self):

        domain_id = "2d1d1d1d-1324-4a80-aa32-1f69a91bf2c8"
        created_at = datetime.datetime(2014, 6, 22, 21, 50, 0)
        updated_at = datetime.datetime(2014, 6, 22, 21, 50, 0)
        serial = 1234567

        # Create a domain
        fixture = self.get_domain_fixture(0)
        fixture['id'] = domain_id
        fixture['created_at'] = created_at
        fixture['updated_at'] = updated_at
        fixture['serial'] = serial

        self.post('domains', data=fixture, status_code=400)

    def test_create_invalid_name(self):
        # Prepare a domain
        fixture = self.get_domain_fixture(0)

        invalid_names = [
            'org',
            'example.org',
            'example.321',
        ]

        for invalid_name in invalid_names:
            fixture['name'] = invalid_name

            # Create a record
            response = self.post('domains', data=fixture, status_code=400)

            self.assertNotIn('id', response.json)

    def test_create_invalid_email(self):
        # Prepare a domain
        fixture = self.get_domain_fixture(0)

        invalid_emails = [
            'org',
            'example.org',
            'bla.example.org',
            'org.',
            'example.org.',
            'bla.example.org.',
            'bla.example.org.',
        ]

        for invalid_email in invalid_emails:
            fixture['email'] = invalid_email

            # Create a record
            response = self.post('domains', data=fixture, status_code=400)

            self.assertNotIn('id', response.json)

    def test_get_domains(self):
        response = self.get('domains')

        self.assertIn('domains', response.json)
        self.assertEqual(0, len(response.json['domains']))

        # Create a domain
        self.create_domain()

        response = self.get('domains')

        self.assertIn('domains', response.json)
        self.assertEqual(1, len(response.json['domains']))

        # Create a second domain
        self.create_domain(fixture=1)

        response = self.get('domains')

        self.assertIn('domains', response.json)
        self.assertEqual(2, len(response.json['domains']))

    def test_get_domain_servers(self):
        # Create a domain
        domain = self.create_domain()
        response = self.get('domains/%s/servers' % domain['id'])
        # Verify length of domain servers
        self.assertEqual(1, len(response.json['servers']))

    @patch.object(central_service.Service,
                  'find_domains',
                  side_effect=messaging.MessagingTimeout())
    def test_get_domains_timeout(self, _):
        self.get('domains', status_code=504)

    def test_get_domain(self):
        # Create a domain
        domain = self.create_domain()

        response = self.get('domains/%s' % domain['id'])

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], domain['id'])

    @patch.object(central_service.Service,
                  'find_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_get_domain_timeout(self, _):
        # Create a domain
        domain = self.create_domain()

        self.get('domains/%s' % domain['id'], status_code=504)

    def test_get_domain_missing(self):
        self.get('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
                 status_code=404)

    def test_get_domain_invalid_id(self):
        # The letter "G" is not valid in a UUID
        self.get('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
                 status_code=404)

        self.get('domains/2fdadfb1cf964259ac6bbb7b6d2ff980', status_code=404)

    def test_update_domain(self):
        # Create a domain
        domain = self.create_domain()

        data = {'email': 'prefix-%s' % domain['email']}

        response = self.put('domains/%s' % domain['id'], data=data)

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], domain['id'])

        self.assertIn('email', response.json)
        self.assertEqual(response.json['email'], 'prefix-%s' % domain['email'])

    def test_update_domain_junk(self):
        # Create a domain
        domain = self.create_domain()

        data = {'email': 'prefix-%s' % domain['email'], 'junk': 'Junk Field'}

        self.put('domains/%s' % domain['id'], data=data, status_code=400)

    def test_update_domain_name_fail(self):
        # Create a domain
        domain = self.create_domain()

        data = {'name': 'renamed.com.'}

        self.put('domains/%s' % domain['id'], data=data, status_code=400)

    def test_update_domain_null_ttl(self):
        # Create a domain
        domain = self.create_domain()

        data = {'ttl': None}

        self.put('domains/%s' % domain['id'], data=data, status_code=400)

    def test_update_domain_negative_ttl(self):
        # Create a domain
        domain = self.create_domain()

        data = {'ttl': -1}

        self.put('domains/%s' % domain['id'], data=data, status_code=400)

    @patch.object(central_service.Service,
                  'update_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_update_domain_timeout(self, _):
        # Create a domain
        domain = self.create_domain()

        data = {'email': 'prefix-%s' % domain['email']}

        self.put('domains/%s' % domain['id'], data=data, status_code=504)

    @patch.object(central_service.Service,
                  'update_domain',
                  side_effect=exceptions.DuplicateDomain())
    def test_update_domain_duplicate(self, _):
        # Create a domain
        domain = self.create_domain()

        data = {'email': 'prefix-%s' % domain['email']}

        self.put('domains/%s' % domain['id'], data=data, status_code=409)

    def test_update_domain_missing(self):
        data = {'email': '*****@*****.**'}

        self.put('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
                 data=data,
                 status_code=404)

    def test_update_domain_invalid_id(self):
        data = {'email': '*****@*****.**'}

        # The letter "G" is not valid in a UUID
        self.put('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
                 data=data,
                 status_code=404)

        self.put('domains/2fdadfb1cf964259ac6bbb7b6d2ff980',
                 data=data,
                 status_code=404)

    def test_delete_domain(self):
        # Create a domain
        domain = self.create_domain()

        self.delete('domains/%s' % domain['id'])

        # Simulate the domain having been deleted on the backend
        domain_serial = self.central_service.get_domain(
            self.admin_context, domain['id']).serial
        self.central_service.update_status(self.admin_context, domain['id'],
                                           "SUCCESS", domain_serial)

        # Ensure we can no longer fetch the domain
        self.get('domains/%s' % domain['id'], status_code=404)

    @patch.object(central_service.Service,
                  'delete_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_delete_domain_timeout(self, _):
        # Create a domain
        domain = self.create_domain()

        self.delete('domains/%s' % domain['id'], status_code=504)

    def test_delete_domain_missing(self):
        self.delete('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
                    status_code=404)

    def test_delete_domain_invalid_id(self):
        # The letter "G" is not valid in a UUID
        self.delete('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
                    status_code=404)

        self.delete('domains/2fdadfb1cf964259ac6bbb7b6d2ff980',
                    status_code=404)

    def test_get_secondary_missing(self):
        fixture = self.get_domain_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        domain = self.create_domain(**fixture)

        self.get('domains/%s' % domain.id, status_code=404)

    def test_update_secondary_missing(self):
        fixture = self.get_domain_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        domain = self.create_domain(**fixture)

        self.put('domains/%s' % domain.id, {}, status_code=404)

    def test_delete_secondary_missing(self):
        fixture = self.get_domain_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        domain = self.create_domain(**fixture)
        self.delete('domains/%s' % domain.id, status_code=404)

    def test_get_domain_servers_from_secondary(self):
        fixture = self.get_domain_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        domain = self.create_domain(**fixture)
        self.get('domains/%s/servers' % domain.id, status_code=404)
Beispiel #21
0
class ApiV2ZonesTest(ApiV2TestCase):
    def setUp(self):
        super(ApiV2ZonesTest, self).setUp()

        # Create the default TLDs
        self.create_default_tlds()

    def test_create_zone(self):
        # Create a zone
        fixture = self.get_zone_fixture(fixture=0)
        response = self.client.post_json('/zones/', fixture)
        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertEqual('PENDING', response.json['status'])
        self.assertEqual('PRIMARY', response.json['type'])
        self.assertEqual([], response.json['masters'])
        self.assertIsNone(response.json['updated_at'])

        for k in fixture:
            self.assertEqual(fixture[k], response.json[k])

    def test_create_zone_no_type(self):
        # Create a zone
        fixture = self.get_zone_fixture(fixture=0)
        del fixture['type']

        response = self.client.post_json('/zones/', fixture)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertEqual('PENDING', response.json['status'])
        self.assertEqual('PRIMARY', response.json['type'])
        self.assertEqual([], response.json['masters'])
        self.assertIsNone(response.json['updated_at'])

        for k in fixture:
            self.assertEqual(fixture[k], response.json[k])

    def test_create_zone_validation(self):
        # NOTE: The schemas should be tested separately to the API. So we
        #       don't need to test every variation via the API itself.
        # Fetch a fixture
        fixture = self.get_zone_fixture(fixture=0)

        # Add a junk field to the body
        fixture['junk'] = 'Junk Field'

        # Ensure it fails with a 400
        body = fixture

        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_email_too_long(self):
        fixture = self.get_zone_fixture(fixture=0)
        fixture.update({'email': 'a' * 255 + '@abc.com'})
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_invalid_email(self):
        invalid_emails = [
            'org',
            'example.org',
            'bla.example.org',
            'org.',
            'example.org.',
            'bla.example.org.',
        ]
        fixture = self.get_zone_fixture(fixture=0)
        for email in invalid_emails:
            fixture.update({'email': email})
            body = fixture
            self._assert_exception('invalid_object', 400,
                                   self.client.post_json, '/zones', body)

    def test_create_zone_email_missing(self):
        fixture = self.get_zone_fixture(fixture=0)
        del fixture['email']
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_ttl_less_than_zero(self):
        fixture = self.get_zone_fixture(fixture=0)
        fixture['ttl'] = -1
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_ttl_is_zero(self):
        fixture = self.get_zone_fixture(fixture=0)
        fixture['ttl'] = 0
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_ttl_is_greater_than_max(self):
        fixture = self.get_zone_fixture(fixture=0)
        fixture['ttl'] = 2174483648
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_ttl_is_invalid(self):
        fixture = self.get_zone_fixture(fixture=0)
        fixture['ttl'] = "!@?>"
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_ttl_is_not_required_field(self):
        fixture = self.get_zone_fixture(fixture=0)
        body = fixture
        response = self.client.post_json('/zones', body)
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

    def test_create_zone_description_too_long(self):
        fixture = self.get_zone_fixture(fixture=0)
        fixture['description'] = "a" * 161
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_name_is_missing(self):
        fixture = self.get_zone_fixture(fixture=0)
        del fixture['name']
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_name_too_long(self):
        fixture = self.get_zone_fixture(fixture=0)
        fixture['name'] = 'x' * 255 + ".com"
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_body_validation(self):
        fixture = self.get_zone_fixture(fixture=0)
        # Add id to the body
        fixture['id'] = '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
        # Ensure it fails with a 400
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

        fixture = self.get_zone_fixture(fixture=0)
        # Add created_at to the body
        fixture['created_at'] = '2014-03-12T19:07:53.000000'
        # Ensure it fails with a 400
        body = fixture
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', body)

    def test_create_zone_invalid_name(self):
        # Try to create a zone with an invalid name
        fixture = self.get_zone_fixture(fixture=-1)

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones', fixture)

    @patch.object(central_service.Service,
                  'create_zone',
                  side_effect=messaging.MessagingTimeout())
    def test_create_zone_timeout(self, _):
        fixture = self.get_zone_fixture(fixture=0)

        body = fixture

        self._assert_exception('timeout', 504, self.client.post_json,
                               '/zones/', body)

    @patch.object(central_service.Service,
                  'create_zone',
                  side_effect=exceptions.DuplicateZone())
    def test_create_zone_duplicate(self, _):
        fixture = self.get_zone_fixture(fixture=0)

        body = fixture

        self._assert_exception('duplicate_zone', 409, self.client.post_json,
                               '/zones/', body)

    def test_create_zone_missing_content_type(self):
        self._assert_exception('unsupported_content_type', 415,
                               self.client.post, '/zones')

    def test_create_zone_bad_content_type(self):
        self._assert_exception('unsupported_content_type',
                               415,
                               self.client.post,
                               '/zones',
                               headers={'Content-type': 'test/goat'})

    def test_zone_invalid_url(self):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980/invalid'
        self._assert_exception('not_found',
                               404,
                               self.client.get,
                               url,
                               headers={'Accept': 'application/json'})
        self._assert_exception('not_found', 404, self.client.patch_json, url)
        self._assert_exception('not_found', 404, self.client.delete, url)

        # Pecan returns a 405 for post
        response = self.client.post(url, status=405)
        self.assertEqual(405, response.status_int)

    def test_get_zones(self):
        response = self.client.get('/zones/')

        # 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('zones', response.json)
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # We should start with 0 zones
        self.assertEqual(0, len(response.json['zones']))

        # We should start with 0 zones
        self.assertEqual(0, len(response.json['zones']))

        data = [self.create_zone(name='x-%s.com.' % i) for i in 'abcdefghij']
        self._assert_paging(data, '/zones', key='zones')

        self._assert_invalid_paging(data, '/zones', key='zones')

    @patch.object(central_service.Service,
                  'find_zones',
                  side_effect=messaging.MessagingTimeout())
    def test_get_zones_timeout(self, _):
        self._assert_exception('timeout', 504, self.client.get, '/zones/')

    def test_get_zone(self):
        # Create a zone
        zone = self.create_zone()

        response = self.client.get('/zones/%s' % zone['id'],
                                   headers=[('Accept', 'application/json')])

        # 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('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertEqual('PENDING', response.json['status'])
        self.assertIsNone(response.json['updated_at'])
        self.assertEqual(zone['name'], response.json['name'])
        self.assertEqual(zone['email'], response.json['email'])

    def test_get_zone_invalid_id(self):
        self._assert_invalid_uuid(self.client.get, '/zones/%s')

    @patch.object(central_service.Service,
                  'get_zone',
                  side_effect=messaging.MessagingTimeout())
    def test_get_zone_timeout(self, _):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
        self._assert_exception('timeout',
                               504,
                               self.client.get,
                               url,
                               headers={'Accept': 'application/json'})

    @patch.object(central_service.Service,
                  'get_zone',
                  side_effect=exceptions.ZoneNotFound())
    def test_get_zone_missing(self, _):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
        self._assert_exception('zone_not_found',
                               404,
                               self.client.get,
                               url,
                               headers={'Accept': 'application/json'})

    def test_get_zone_bad_accept(self):
        url = '/zones/6e2146f3-87bc-4f47-adc5-4df0a5c78218'

        self.client.get(url, headers={'Accept': 'test/goat'}, status=406)

    def test_update_zone(self):
        # Create a zone
        zone = self.create_zone()

        # Prepare an update body
        body = {'email': 'prefix-%s' % zone['email']}

        response = self.client.patch_json('/zones/%s' % zone['id'],
                                          body,
                                          status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])
        self.assertIn('status', response.json)

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIsNotNone(response.json['updated_at'])
        self.assertEqual('prefix-%s' % zone['email'], response.json['email'])

    def test_update_zone_invalid_id(self):
        self._assert_invalid_uuid(self.client.patch_json, '/zones/%s')

    def test_update_zone_validation(self):
        # NOTE: The schemas should be tested separatly to the API. So we
        #       don't need to test every variation via the API itself.
        # Create a zone
        zone = self.create_zone()

        # Prepare an update body with junk in the body
        body = {'email': 'prefix-%s' % zone['email'], 'junk': 'Junk Field'}

        url = '/zones/%s' % zone['id']

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.patch_json,
                               url, body)

        # Prepare an update body with negative ttl in the body
        body = {'email': 'prefix-%s' % zone['email'], 'ttl': -20}

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.patch_json,
                               url, body)

        # Prepare an update body with ttl > maximum (2147483647) in the body
        body = {'email': 'prefix-%s' % zone['email'], 'ttl': 2147483648}

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.patch_json,
                               url, body)

    @patch.object(central_service.Service,
                  'get_zone',
                  side_effect=exceptions.DuplicateZone())
    def test_update_zone_duplicate(self, _):
        # Prepare an update body
        body = {'email': '*****@*****.**'}

        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 409
        self._assert_exception('duplicate_zone', 409, self.client.patch_json,
                               url, body)

    @patch.object(central_service.Service,
                  'get_zone',
                  side_effect=messaging.MessagingTimeout())
    def test_update_zone_timeout(self, _):
        # Prepare an update body
        body = {'email': '*****@*****.**'}

        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 504
        self._assert_exception('timeout', 504, self.client.patch_json, url,
                               body)

    @patch.object(central_service.Service,
                  'get_zone',
                  side_effect=exceptions.ZoneNotFound())
    def test_update_zone_missing(self, _):
        # Prepare an update body
        body = {'email': '*****@*****.**'}

        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        # Ensure it fails with a 404
        self._assert_exception('zone_not_found', 404, self.client.patch_json,
                               url, body)

    def test_delete_zone(self):
        zone = self.create_zone()

        response = self.client.delete('/zones/%s' % zone['id'], status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual('DELETE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

        # The deleted zone should still be listed
        zones = self.client.get('/zones/')
        self.assertEqual(1, len(zones.json['zones']))

    def test_delete_zone_invalid_id(self):
        self._assert_invalid_uuid(self.client.delete, '/zones/%s')

    @patch.object(central_service.Service,
                  'delete_zone',
                  side_effect=messaging.MessagingTimeout())
    def test_delete_zone_timeout(self, _):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        self._assert_exception('timeout', 504, self.client.delete, url)

    @patch.object(central_service.Service,
                  'delete_zone',
                  side_effect=exceptions.ZoneNotFound())
    def test_delete_zone_missing(self, _):
        url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

        self._assert_exception('zone_not_found', 404, self.client.delete, url)

    def test_post_abandon_zone(self):
        zone = self.create_zone()
        url = '/zones/%s/tasks/abandon' % zone.id

        # Ensure that we get permission denied
        self._assert_exception('forbidden', 403, self.client.post_json, url)

        # Ensure that abandon zone succeeds with the right policy
        self.policy({'abandon_zone': '@'})
        response = self.client.post_json(url)
        self.assertEqual(204, response.status_int)

    def test_get_abandon_zone(self):
        zone = self.create_zone()
        url = '/zones/%s/tasks/abandon' % zone.id
        self._assert_exception('method_not_allowed', 405, self.client.get, url)

    def test_get_invalid_abandon(self):
        # This is an invalid endpoint - should return 404
        url = '/zones/tasks/abandon'
        self._assert_exception('not_found', 404, self.client.get, url)

    def test_get_zone_tasks(self):
        # This is an invalid endpoint - should return 404
        zone = self.create_zone()
        url = '/zones/%s/tasks' % zone.id
        self._assert_exception('not_found', 404, self.client.get, url)

    def test_create_secondary(self):
        # Create a zone
        fixture = self.get_zone_fixture('SECONDARY', 0)
        fixture['masters'] = ["10.0.0.1"]

        response = self.client.post_json('/zones/', fixture)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertEqual('PENDING', response.json['status'])
        self.assertEqual(cfg.CONF['service:central'].managed_resource_email,
                         response.json['email'])

        self.assertIsNone(response.json['updated_at'])
        # Zone is not transferred yet
        self.assertIsNone(response.json['transferred_at'])
        # Serial defaults to 1
        self.assertEqual(response.json['serial'], 1)

        for k in fixture:
            self.assertEqual(fixture[k], response.json[k])

    def test_create_secondary_no_masters(self):
        # Create a zone
        fixture = self.get_zone_fixture('SECONDARY', 0)

        self._assert_exception('invalid_object', 400, self.client.post_json,
                               '/zones/', fixture)

    def test_update_secondary(self):
        # Create a zone
        zone = objects.Zone(name='example.com.',
                            type='SECONDARY',
                            masters=objects.ZoneMasterList.from_list([{
                                'host':
                                '1.0.0.0',
                                'port':
                                69
                            }, {
                                'host':
                                '2.0.0.0',
                                'port':
                                69
                            }]))
        zone.email = cfg.CONF['service:central'].managed_resource_email

        # Create a zone
        zone = self.central_service.create_zone(self.admin_context, zone)

        masters = ['10.0.0.1', '10.0.0.2']

        # Prepare an update body
        body = {'masters': masters}

        response = self.client.patch_json('/zones/%s' % zone['id'],
                                          body,
                                          status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the body structure is what we expect
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])
        self.assertIn('status', response.json)

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIsNotNone(response.json['updated_at'])
        self.assertEqual(masters, response.json['masters'])
        self.assertEqual(1, response.json['serial'])

    def test_xfr_request(self):
        # Create a zone
        fixture = self.get_zone_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email
        fixture['masters'] = [{"host": "10.0.0.10", "port": 53}]

        # Create a zone
        zone = self.create_zone(**fixture)

        mdns = mock.Mock()
        with mock.patch.object(mdns_api.MdnsAPI, 'get_instance') as get_mdns:
            get_mdns.return_value = mdns
            mdns.get_serial_number.return_value = (
                'SUCCESS',
                10,
                1,
            )

            response = self.client.post_json('/zones/%s/tasks/xfr' %
                                             zone['id'],
                                             None,
                                             status=202)

        self.assertTrue(mdns.perform_zone_xfr.called)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)
        self.assertEqual(b'""', response.body)

    def test_invalid_xfr_request(self):
        # Create a zone
        zone = self.create_zone()

        response = self.client.post_json('/zones/%s/tasks/xfr' % zone['id'],
                                         None,
                                         status=400)

        # Check the headers are what we expect
        self.assertEqual(400, response.status_int)
        self.assertEqual('application/json', response.content_type)

    def test_update_secondary_email_invalid_object(self):
        # Create a zone
        fixture = self.get_zone_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        # Create a zone
        zone = self.create_zone(**fixture)

        body = {'email': '*****@*****.**'}

        self._assert_exception('invalid_object', 400, self.client.patch_json,
                               '/zones/%s' % zone['id'], body)

    # Metadata tests
    def test_metadata_exists(self):
        response = self.client.get('/zones/')

        # Make sure the fields exist
        self.assertIn('metadata', response.json)
        self.assertIn('total_count', response.json['metadata'])

    def test_total_count(self):
        response = self.client.get('/zones/')

        # There are no zones by default
        self.assertEqual(0, response.json['metadata']['total_count'])

        # Create a zone
        fixture = self.get_zone_fixture(fixture=0)
        response = self.client.post_json('/zones/', fixture)

        response = self.client.get('/zones/')

        # Make sure total_count picked it up
        self.assertEqual(1, response.json['metadata']['total_count'])

    def test_total_count_pagination(self):
        # Create two zones
        fixture = self.get_zone_fixture(fixture=0)
        response = self.client.post_json('/zones/', fixture)

        fixture = self.get_zone_fixture(fixture=1)
        response = self.client.post_json('/zones/', fixture)

        # Paginate so that there is only one zone returned
        response = self.client.get('/zones?limit=1')

        self.assertEqual(1, len(response.json['zones']))

        # The total_count should know there are two
        self.assertEqual(2, response.json['metadata']['total_count'])

    def test_no_update_deleting(self):
        # Create a zone
        zone = self.create_zone()

        # Prepare an update body
        body = {'zone': {'email': 'prefix-%s' % zone['email']}}

        self.client.delete('/zones/%s' % zone['id'], status=202)
        self._assert_exception('bad_request', 400, self.client.patch_json,
                               '/zones/%s' % zone['id'], body)

    def test_get_nameservers(self):
        # Create a zone
        zone = self.create_zone()

        # Prepare an update body

        response = self.client.get('/zones/%s/nameservers' % zone['id'],
                                   headers=[('Accept', 'application/json')])

        self.assertIn('nameservers', response.json)
        self.assertEqual(1, len(response.json['nameservers']))
        self.assertIn('hostname', response.json['nameservers'][0])
        self.assertIn('priority', response.json['nameservers'][0])

    def test_get_zones_filter(self):
        # Add zones for testing
        fixtures = [
            self.get_zone_fixture('PRIMARY',
                                  fixture=0,
                                  values={
                                      'ttl': 3600,
                                      'description': 'test1'
                                  }),
            self.get_zone_fixture('PRIMARY',
                                  fixture=1,
                                  values={
                                      'ttl': 4000,
                                      'description': 'test2'
                                  })
        ]

        for fixture in fixtures:
            response = self.client.post_json('/zones/', fixture)

        get_urls = [
            # Filter by Type
            '/zones?type=%s' % fixtures[0]['type'],

            # Filter by Name
            '/zones?name=%s' % fixtures[0]['name'],

            # Filter by Email
            '/zones?email=example*',
            '/zones?email=%s' % fixtures[1]['email'],

            # Filter by TTL
            '/zones?ttl=3600',

            # Filter by Description
            '/zones?description=test1',
            '/zones?description=test*'
        ]

        correct_results = [2, 1, 2, 1, 1, 1, 2]

        for get_url, correct_result in zip(get_urls, correct_results):

            response = self.client.get(get_url)

            # Check the headers are what we expect
            self.assertEqual(200, response.status_int)
            self.assertEqual('application/json', response.content_type)

            # Check that the correct number of zones match
            self.assertEqual(correct_result, len(response.json['zones']))

    def test_invalid_zones_filter(self):
        invalid_url = '/zones?id=155477ef-e6c5-4b94-984d-8fc68c0c1a14'
        self._assert_exception('bad_request', 400, self.client.get,
                               invalid_url)
Beispiel #22
0
class SchedulerClientTestCase(test.NoDBTestCase):
    def setUp(self):
        super(SchedulerClientTestCase, self).setUp()
        self.client = scheduler_client.SchedulerClient()

    def test_constructor(self):
        self.assertIsNotNone(self.client.queryclient)
        self.assertIsNotNone(self.client.reportclient)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'select_destinations')
    def test_select_destinations(self, mock_select_destinations):
        fake_spec = objects.RequestSpec()
        self.assertIsNone(self.client.queryclient.instance)

        self.client.select_destinations('ctxt', fake_spec)

        self.assertIsNotNone(self.client.queryclient.instance)
        mock_select_destinations.assert_called_once_with('ctxt', fake_spec)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'select_destinations',
                       side_effect=messaging.MessagingTimeout())
    def test_select_destinations_timeout(self, mock_select_destinations):
        # check if the scheduler service times out properly
        fake_spec = objects.RequestSpec()
        fake_args = ['ctxt', fake_spec]
        self.assertRaises(messaging.MessagingTimeout,
                          self.client.select_destinations, *fake_args)
        mock_select_destinations.assert_has_calls([mock.call(*fake_args)] * 2)

    @mock.patch.object(
        scheduler_query_client.SchedulerQueryClient,
        'select_destinations',
        side_effect=[messaging.MessagingTimeout(), mock.DEFAULT])
    def test_select_destinations_timeout_once(self, mock_select_destinations):
        # scenario: the scheduler service times out & recovers after failure
        fake_spec = objects.RequestSpec()
        fake_args = ['ctxt', fake_spec]
        self.client.select_destinations(*fake_args)
        mock_select_destinations.assert_has_calls([mock.call(*fake_args)] * 2)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'update_aggregates')
    def test_update_aggregates(self, mock_update_aggs):
        aggregates = [objects.Aggregate(id=1)]
        self.client.update_aggregates(context='context', aggregates=aggregates)
        mock_update_aggs.assert_called_once_with('context', aggregates)

    @mock.patch.object(scheduler_query_client.SchedulerQueryClient,
                       'delete_aggregate')
    def test_delete_aggregate(self, mock_delete_agg):
        aggregate = objects.Aggregate(id=1)
        self.client.delete_aggregate(context='context', aggregate=aggregate)
        mock_delete_agg.assert_called_once_with('context', aggregate)

    @mock.patch.object(scheduler_report_client.SchedulerReportClient,
                       'update_resource_stats')
    def test_update_resource_stats(self, mock_update_resource_stats):
        self.assertIsNone(self.client.reportclient.instance)

        self.client.update_resource_stats(mock.sentinel.cn)

        self.assertIsNotNone(self.client.reportclient.instance)
        mock_update_resource_stats.assert_called_once_with(mock.sentinel.cn)
Beispiel #23
0
class ApiV1RecordsTest(ApiV1Test):
    def setUp(self):
        super(ApiV1RecordsTest, self).setUp()

        self.domain = self.create_domain()
        self.recordset = self.create_recordset(self.domain, 'A')

    def test_create_record(self):
        recordset_fixture = self.get_recordset_fixture(self.domain['name'])

        fixture = self.get_record_fixture(recordset_fixture['type'])
        fixture.update({
            'name': recordset_fixture['name'],
            'type': recordset_fixture['type'],
        })

        # Create a record
        response = self.post('domains/%s/records' % self.domain['id'],
                             data=fixture)

        self.assertIn('id', response.json)
        self.assertIn('name', response.json)
        self.assertEqual(response.json['name'], fixture['name'])

    def test_create_record_existing_recordset(self):
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Create a record
        response = self.post('domains/%s/records' % self.domain['id'],
                             data=fixture)

        self.assertIn('id', response.json)
        self.assertIn('name', response.json)
        self.assertEqual(response.json['name'], fixture['name'])

    def test_create_record_name_reuse(self):
        fixture_1 = self.get_record_fixture(self.recordset['type'])
        fixture_1.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        fixture_2 = self.get_record_fixture(self.recordset['type'], fixture=1)
        fixture_2.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Create 2 records
        record_1 = self.post('domains/%s/records' % self.domain['id'],
                             data=fixture_1)
        record_2 = self.post('domains/%s/records' % self.domain['id'],
                             data=fixture_2)

        # Delete record 1, this should not have any side effects
        self.delete('domains/%s/records/%s' %
                    (self.domain['id'], record_1.json['id']))

        # Simulate the record 1 having been deleted on the backend
        domain_serial = self.central_service.get_domain(
            self.admin_context, self.domain['id']).serial
        self.central_service.update_status(self.admin_context,
                                           self.domain['id'], "SUCCESS",
                                           domain_serial)

        # Get the record 2 to ensure recordset did not get deleted
        rec_2_get_response = self.get('domains/%s/records/%s' %
                                      (self.domain['id'], record_2.json['id']))

        self.assertIn('id', rec_2_get_response.json)
        self.assertIn('name', rec_2_get_response.json)
        self.assertEqual(rec_2_get_response.json['name'], fixture_1['name'])

        # Delete record 2, this should delete the null recordset too
        self.delete('domains/%s/records/%s' %
                    (self.domain['id'], record_2.json['id']))

        # Simulate the record 2 having been deleted on the backend
        domain_serial = self.central_service.get_domain(
            self.admin_context, self.domain['id']).serial
        self.central_service.update_status(self.admin_context,
                                           self.domain['id'], "SUCCESS",
                                           domain_serial)

        # Re-create as a different type, but use the same name
        fixture = self.get_record_fixture('CNAME')
        fixture.update({'name': self.recordset['name'], 'type': 'CNAME'})

        response = self.post('domains/%s/records' % self.domain['id'],
                             data=fixture)

        self.assertIn('id', response.json)
        self.assertIn('name', response.json)
        self.assertEqual(response.json['name'], fixture['name'])

    def test_create_record_junk(self):
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Add a junk property
        fixture['junk'] = 'Junk Field'

        # Create a record, Ensuring it fails with a 400
        self.post('domains/%s/records' % self.domain['id'],
                  data=fixture,
                  status_code=400)

    def test_create_wildcard_record_after_named(self):
        # We want to test that a wildcard record rs doesnt use the previous one
        # https://bugs.launchpad.net/designate/+bug/1391426

        name = "foo.%s" % self.domain.name
        fixture = {"name": name, "type": "A", "data": "10.0.0.1"}

        self.post('domains/%s/records' % self.domain['id'], data=fixture)

        wildcard_name = '*.%s' % self.domain["name"]

        fixture['name'] = wildcard_name
        self.post('domains/%s/records' % self.domain['id'], data=fixture)

        named_rs = self.central_service.find_recordset(self.admin_context,
                                                       {"name": name})
        wildcard_rs = self.central_service.find_recordset(
            self.admin_context, {"name": wildcard_name})

        self.assertNotEqual(named_rs.name, wildcard_rs.name)
        self.assertNotEqual(named_rs.id, wildcard_rs.id)

    def test_create_record_utf_description(self):
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Add a UTF-8 riddled description
        fixture['description'] = "utf-8:2H₂+O₂⇌2H₂O,R=4.7kΩ,⌀200mm∮E⋅da=Q,n" \
                                 ",∑f(i)=∏g(i),∀x∈ℝ:⌈x⌉"

        # Create a record, Ensuring it succeeds
        self.post('domains/%s/records' % self.domain['id'], data=fixture)

    def test_create_record_description_too_long(self):
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Add a description that is too long
        fixture['description'] = "x" * 161

        # Create a record, Ensuring it Fails with a 400
        self.post('domains/%s/records' % self.domain['id'],
                  data=fixture,
                  status_code=400)

    def test_create_record_negative_ttl(self):
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Set the TTL to a negative value
        fixture['ttl'] = -1

        # Create a record, Ensuring it Fails with a 400
        self.post('domains/%s/records' % self.domain['id'],
                  data=fixture,
                  status_code=400)

    def test_create_record_invalid_ttl(self):
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Set the TTL to a invalid value
        fixture['ttl'] = "$?!."

        # Create a record, Ensuring it Fails with a 400
        self.post('domains/%s/records' % self.domain['id'],
                  data=fixture,
                  status_code=400)

    @patch.object(central_service.Service,
                  'create_record',
                  side_effect=messaging.MessagingTimeout())
    def test_create_record_timeout(self, _):
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Create a record
        self.post('domains/%s/records' % self.domain['id'],
                  data=fixture,
                  status_code=504)

    def test_create_wildcard_record(self):
        # Prepare a record
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': '*.%s' % self.recordset['name'],
            'type': self.recordset['type'],
        })

        # Create a record
        response = self.post('domains/%s/records' % self.domain['id'],
                             data=fixture)

        self.assertIn('id', response.json)
        self.assertIn('name', response.json)
        self.assertEqual(response.json['name'], fixture['name'])

    def test_create_srv_record(self):
        recordset_fixture = self.get_recordset_fixture(self.domain['name'],
                                                       'SRV')

        fixture = self.get_record_fixture(recordset_fixture['type'])
        priority, _, data = fixture['data'].partition(" ")

        fixture.update({
            'data': data,
            'priority': int(priority),
            'name': recordset_fixture['name'],
            'type': recordset_fixture['type'],
        })

        # Create a record
        response = self.post('domains/%s/records' % self.domain['id'],
                             data=fixture)

        self.assertIn('id', response.json)
        self.assertEqual(response.json['type'], fixture['type'])
        self.assertEqual(response.json['name'], fixture['name'])

        self.assertEqual(response.json['priority'], fixture['priority'])
        self.assertEqual(response.json['data'], fixture['data'])

    def test_create_invalid_data_srv_record(self):
        recordset_fixture = self.get_recordset_fixture(self.domain['name'],
                                                       'SRV')

        fixture = self.get_record_fixture(recordset_fixture['type'])
        fixture.update({
            'name': recordset_fixture['name'],
            'type': recordset_fixture['type'],
        })

        invalid_datas = [
            'I 5060 sip.%s' % self.domain['name'],
            '5060 sip.%s' % self.domain['name'],
            '5060 I sip.%s' % self.domain['name'],
            '0 5060 sip',
            'sip',
            'sip.%s' % self.domain['name'],
        ]

        for invalid_data in invalid_datas:
            fixture['data'] = invalid_data
            # Attempt to create the record
            self.post('domains/%s/records' % self.domain['id'],
                      data=fixture,
                      status_code=400)

    def test_create_invalid_name_srv_record(self):
        recordset_fixture = self.get_recordset_fixture(self.domain['name'],
                                                       'SRV')

        fixture = self.get_record_fixture(recordset_fixture['type'])
        fixture.update({
            'name': recordset_fixture['name'],
            'type': recordset_fixture['type'],
        })

        invalid_names = [
            '%s' % self.domain['name'],
            '_udp.%s' % self.domain['name'],
            'sip._udp.%s' % self.domain['name'],
            '_sip.udp.%s' % self.domain['name'],
        ]

        for invalid_name in invalid_names:
            fixture['name'] = invalid_name

            # Attempt to create the record
            self.post('domains/%s/records' % self.domain['id'],
                      data=fixture,
                      status_code=400)

    def test_create_invalid_name(self):
        # Prepare a record
        fixture = self.get_record_fixture(self.recordset['type'])
        fixture.update({
            'name': self.recordset['name'],
            'type': self.recordset['type'],
        })

        invalid_names = [
            'org',
            'example.org',
            '$$.example.org',
            '*example.org.',
            '*.*.example.org.',
            'abc.*.example.org.',
        ]

        for invalid_name in invalid_names:
            fixture['name'] = invalid_name

            # Create a record
            response = self.post('domains/%s/records' % self.domain['id'],
                                 data=fixture,
                                 status_code=400)

            self.assertNotIn('id', response.json)

    def test_get_records(self):
        response = self.get('domains/%s/records' % self.domain['id'])

        # Verify that the SOA & NS records are already created
        self.assertIn('records', response.json)
        self.assertEqual(2, len(response.json['records']))

        # Create a record
        self.create_record(self.domain, self.recordset)

        response = self.get('domains/%s/records' % self.domain['id'])

        # Verify that one more record has been added
        self.assertIn('records', response.json)
        self.assertEqual(3, len(response.json['records']))

        # Create a second record
        self.create_record(self.domain, self.recordset, fixture=1)

        response = self.get('domains/%s/records' % self.domain['id'])

        # Verfiy that all 4 records are there
        self.assertIn('records', response.json)
        self.assertEqual(4, len(response.json['records']))

    @patch.object(central_service.Service,
                  'find_records',
                  side_effect=messaging.MessagingTimeout())
    def test_get_records_timeout(self, _):
        self.get('domains/%s/records' % self.domain['id'], status_code=504)

    def test_get_records_missing_domain(self):
        self.get('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980/records',
                 status_code=404)

    def test_get_records_invalid_domain_id(self):
        self.get('domains/2fdadfb1cf964259ac6bbb7b6d2ff980/records',
                 status_code=404)

    def test_get_record_missing(self):
        self.get('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
                 'bb7b6d2ff980' % self.domain['id'],
                 status_code=404)

    def test_get_record_with_invalid_id(self):
        self.get('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
                 'bb7b6d2ff980GH' % self.domain['id'],
                 status_code=404)

    def test_get_record(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        response = self.get('domains/%s/records/%s' %
                            (self.domain['id'], record['id']))

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], record['id'])
        self.assertEqual(response.json['name'], self.recordset['name'])
        self.assertEqual(response.json['type'], self.recordset['type'])

    def test_update_record(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        # Fetch another fixture to use in the update
        fixture = self.get_record_fixture(self.recordset['type'], fixture=1)

        # Update the record
        data = {'data': fixture['data']}
        response = self.put('domains/%s/records/%s' %
                            (self.domain['id'], record['id']),
                            data=data)

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], record['id'])
        self.assertEqual(response.json['data'], fixture['data'])
        self.assertEqual(response.json['type'], self.recordset['type'])

    def test_update_record_ttl(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        # Update the record
        data = {'ttl': 100}
        response = self.put('domains/%s/records/%s' %
                            (self.domain['id'], record['id']),
                            data=data)

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], record['id'])
        self.assertEqual(response.json['data'], record['data'])
        self.assertEqual(response.json['type'], self.recordset['type'])
        self.assertEqual(response.json['ttl'], 100)

    def test_update_record_junk(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        data = {'ttl': 100, 'junk': 'Junk Field'}

        self.put('domains/%s/records/%s' % (self.domain['id'], record['id']),
                 data=data,
                 status_code=400)

    def test_update_record_negative_ttl(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        data = {'ttl': -1}

        self.put('domains/%s/records/%s' % (self.domain['id'], record['id']),
                 data=data,
                 status_code=400)

    def test_update_record_invalid_ttl(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        data = {'ttl': "$?>%"}

        self.put('domains/%s/records/%s' % (self.domain['id'], record['id']),
                 data=data,
                 status_code=400)

    def test_update_record_outside_domain_fail(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        data = {'name': 'test.someotherdomain.com.'}

        self.put('domains/%s/records/%s' % (self.domain['id'], record['id']),
                 data=data,
                 status_code=400)

    @patch.object(central_service.Service,
                  'find_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_update_record_timeout(self, _):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        data = {'name': 'test.example.org.'}

        self.put('domains/%s/records/%s' % (self.domain['id'], record['id']),
                 data=data,
                 status_code=504)

    def test_update_record_missing(self):
        data = {'name': 'test.example.org.'}

        self.put('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
                 'bb7b6d2ff980' % self.domain['id'],
                 data=data,
                 status_code=404)

    def test_update_record_invalid_id(self):
        data = {'name': 'test.example.org.'}

        self.put('domains/%s/records/2fdadfb1cf964259ac6bbb7b6d2ff980' %
                 self.domain['id'],
                 data=data,
                 status_code=404)

    def test_update_record_missing_domain(self):
        data = {'name': 'test.example.org.'}

        self.put(
            'domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980/records/'
            '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
            data=data,
            status_code=404)

    def test_update_record_invalid_domain_id(self):
        data = {'name': 'test.example.org.'}

        self.put(
            'domains/2fdadfb1cf964259ac6bbb7b6d2ff980/records/'
            '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
            data=data,
            status_code=404)

    def test_delete_record(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        self.delete('domains/%s/records/%s' %
                    (self.domain['id'], record['id']))

        # Simulate the record having been deleted on the backend
        domain_serial = self.central_service.get_domain(
            self.admin_context, self.domain['id']).serial
        self.central_service.update_status(self.admin_context,
                                           self.domain['id'], "SUCCESS",
                                           domain_serial)

        # Ensure we can no longer fetch the record
        self.get('domains/%s/records/%s' % (self.domain['id'], record['id']),
                 status_code=404)

    @patch.object(central_service.Service,
                  'find_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_delete_record_timeout(self, _):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        self.delete('domains/%s/records/%s' %
                    (self.domain['id'], record['id']),
                    status_code=504)

    def test_delete_record_missing(self):
        self.delete('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
                    'bb7b6d2ff980' % self.domain['id'],
                    status_code=404)

    def test_delete_record_missing_domain(self):
        self.delete(
            'domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980/records/'
            '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
            status_code=404)

    def test_delete_record_invalid_domain_id(self):
        self.delete(
            'domains/2fdadfb1cf964259ac6bbb7b6d2ff980/records/'
            '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
            status_code=404)

    def test_delete_record_invalid_id(self):
        self.delete('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
                    'bb7b6d2ff980GH' % self.domain['id'],
                    status_code=404)

    def test_get_record_in_secondary(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = "*****@*****.**"

        domain = self.create_domain(**fixture)

        record = self.create_record(domain, self.recordset)

        url = 'domains/%s/records/%s' % (domain.id, record.id)
        self.get(url, status_code=404)

    def test_create_record_in_secondary(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = "*****@*****.**"

        domain = self.create_domain(**fixture)

        record = {
            "name": "foo.%s" % domain.name,
            "type": "A",
            "data": "10.0.0.1"
        }

        url = 'domains/%s/records' % domain.id
        self.post(url, record, status_code=404)

    def test_update_record_in_secondary(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = "*****@*****.**"

        domain = self.create_domain(**fixture)

        record = self.create_record(domain, self.recordset)

        url = 'domains/%s/records/%s' % (domain.id, record.id)
        self.put(url, {"data": "10.0.0.1"}, status_code=404)

    def test_delete_record_in_secondary(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = "*****@*****.**"

        domain = self.create_domain(**fixture)

        record = self.create_record(domain, self.recordset)

        url = 'domains/%s/records/%s' % (domain.id, record.id)
        self.delete(url, status_code=404)

    def test_create_record_deleting_domain(self):
        recordset_fixture = self.get_recordset_fixture(self.domain['name'])

        fixture = self.get_record_fixture(recordset_fixture['type'])
        fixture.update({
            'name': recordset_fixture['name'],
            'type': recordset_fixture['type'],
        })

        self.delete('/domains/%s' % self.domain['id'])
        self.post('domains/%s/records' % self.domain['id'],
                  data=fixture,
                  status_code=400)

    def test_update_record_deleting_domain(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        # Fetch another fixture to use in the update
        fixture = self.get_record_fixture(self.recordset['type'], fixture=1)

        # Update the record
        data = {'data': fixture['data']}
        self.delete('/domains/%s' % self.domain['id'])
        self.put('domains/%s/records/%s' % (self.domain['id'], record['id']),
                 data=data,
                 status_code=400)

    def test_delete_record_deleting_domain(self):
        # Create a record
        record = self.create_record(self.domain, self.recordset)

        self.delete('/domains/%s' % self.domain['id'])
        self.delete('domains/%s/records/%s' %
                    (self.domain['id'], record['id']),
                    status_code=400)
Beispiel #24
0
class ApiV1ServersTest(ApiV1Test):
    def setUp(self):
        super(ApiV1ServersTest, self).setUp()

        # All Server Checks should be performed as an admin, so..
        # Override to policy to make everyone an admin.

        self.policy({'admin': '@'})

    def test_create_server(self):
        # Create a server
        fixture = self.get_server_fixture(0)

        response = self.post('servers', data=fixture)

        self.assertIn('id', response.json)
        self.assertIn('name', response.json)
        self.assertEqual(response.json['name'], fixture['name'])

    def test_create_server_junk(self):
        # Create a server
        fixture = self.get_server_fixture(0)

        # Add a junk property
        fixture['junk'] = 'Junk Field'

        # Ensure it fails with a 400
        self.post('servers', data=fixture, status_code=400)

    def test_create_server_with_invalid_name(self):
        # Create a server
        fixture = self.get_server_fixture(0)

        # Add a invalid name
        fixture['name'] = '$#$%^^'

        # Ensure it fails with a 400
        self.post('servers', data=fixture, status_code=400)

    @patch.object(central_service.Service,
                  'update_pool',
                  side_effect=messaging.MessagingTimeout())
    def test_create_server_timeout(self, _):
        # Create a server
        fixture = self.get_server_fixture(0)

        self.post('servers', data=fixture, status_code=504)

    @patch.object(central_service.Service,
                  'update_pool',
                  side_effect=exceptions.DuplicateServer())
    def test_create_server_duplicate(self, _):
        # Create a server
        fixture = self.get_server_fixture(0)

        self.post('servers', data=fixture, status_code=409)

    def test_get_servers(self):
        # Fetch the default pool
        pool = self.storage.get_pool(self.admin_context, default_pool_id)

        # Fetch the list of servers
        response = self.get('servers')

        self.assertIn('servers', response.json)
        self.assertEqual(len(pool.ns_records), len(response.json['servers']))

        # Add a new NS record to the pool
        pool.ns_records.append(
            objects.PoolNsRecord(priority=0, hostname='new-ns1.example.org.'))

        # Save the pool to add a new nameserver
        self.storage.update_pool(self.admin_context, pool)

        # Fetch the list of servers
        response = self.get('servers')

        self.assertIn('servers', response.json)
        self.assertEqual(len(pool.ns_records), len(response.json['servers']))

        # Add a new NS record to the pool
        pool.ns_records.append(
            objects.PoolNsRecord(priority=0, hostname='new-ns2.example.org.'))

        # Save the pool to add a new nameserver
        self.storage.update_pool(self.admin_context, pool)

        response = self.get('servers')

        self.assertIn('servers', response.json)
        self.assertEqual(len(pool.ns_records), len(response.json['servers']))

    @patch.object(central_service.Service,
                  'get_pool',
                  side_effect=messaging.MessagingTimeout())
    def test_get_servers_timeout(self, _):
        self.get('servers', status_code=504)

    def test_get_server(self):
        # Fetch the default pool
        pool = self.storage.get_pool(self.admin_context, default_pool_id)

        # Fetch the Server from the pool
        response = self.get('servers/%s' % pool.ns_records[0].id)

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], pool.ns_records[0]['id'])

    @patch.object(central_service.Service,
                  'get_pool',
                  side_effect=messaging.MessagingTimeout())
    def test_get_server_timeout(self, _):
        # Fetch the default pool
        pool = self.storage.get_pool(self.admin_context, default_pool_id)

        self.get('servers/%s' % pool.ns_records[0].id, status_code=504)

    def test_get_server_with_invalid_id(self):
        self.get('servers/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff98GH',
                 status_code=404)

    def test_get_server_missing(self):
        self.get('servers/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
                 status_code=404)

    def test_update_server(self):
        # Fetch the default pool
        pool = self.storage.get_pool(self.admin_context, default_pool_id)

        data = {'name': 'new-ns1.example.org.'}

        response = self.put('servers/%s' % pool.ns_records[0].id, data=data)

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], pool.ns_records[0].id)

        self.assertIn('name', response.json)
        self.assertEqual(response.json['name'], 'new-ns1.example.org.')

    def test_update_server_missing(self):
        data = {'name': 'test.example.org.'}
        self.put('servers/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
                 data=data,
                 status_code=404)

    def test_update_server_junk(self):
        # Fetch the default pool
        pool = self.storage.get_pool(self.admin_context, default_pool_id)

        data = {'name': 'test.example.org.', 'junk': 'Junk Field'}

        self.put('servers/%s' % pool.ns_records[0].id,
                 data=data,
                 status_code=400)

    def test_delete_server(self):
        # Fetch the default pool
        pool = self.storage.get_pool(self.admin_context, default_pool_id)

        # Create a second server so that we can delete the first
        # because the last remaining server is not allowed to be deleted
        # Add a new NS record to the pool
        pool.ns_records.append(
            objects.PoolNsRecord(priority=0, hostname='new-ns2.example.org.'))

        # Save the pool to add a new nameserver
        self.storage.update_pool(self.admin_context, pool)

        # Now delete the server
        self.delete('servers/%s' % pool.ns_records[1].id)

        # Ensure we can no longer fetch the deleted server
        self.get('servers/%s' % pool.ns_records[1].id, status_code=404)

        # Also, verify we cannot delete last remaining server
        self.delete('servers/%s' % pool.ns_records[0].id, status_code=400)

    def test_delete_server_with_invalid_id(self):
        self.delete('servers/9fdadfb1-cf96-4259-ac6b-bb7b6d2ff98GH',
                    status_code=404)

    def test_delete_server_missing(self):
        self.delete('servers/9fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
                    status_code=404)
Beispiel #25
0
class ApiV2RecordSetsTest(ApiV2TestCase):
    def setUp(self):
        super(ApiV2RecordSetsTest, self).setUp()

        # Create a domain
        self.domain = self.create_domain()

    def test_create_recordset(self):
        # Prepare a RecordSet fixture
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        response = self.client.post_json(
            '/zones/%s/recordsets' % self.domain['id'], fixture)

        # 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('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertIsNone(response.json['updated_at'])
        self.assertIn('records', response.json)
        # The action and status are NONE and ACTIVE as there are no records
        self.assertEqual('NONE', response.json['action'])
        self.assertEqual('ACTIVE', response.json['status'])

    def test_create_recordset_with_records(self):
        # Prepare a RecordSet fixture
        fixture = self.get_recordset_fixture(
            self.domain['name'], 'A', fixture=0, values={'records': [
                '192.0.2.1',
                '192.0.2.2',
            ]}
        )

        response = self.client.post_json(
            '/zones/%s/recordsets' % self.domain['id'], fixture)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the values returned are what we expect
        self.assertIn('records', response.json)
        self.assertEqual(2, len(response.json['records']))
        self.assertEqual('CREATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

        # Check the zone's status is as expected
        response = self.client.get('/zones/%s' % self.domain['id'],
                                   headers=[('Accept', 'application/json')])
        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertEqual('UPDATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

    def test_create_recordset_with_invalid_name(self):
        # Prepare a RecordSet fixture
        body = self.get_recordset_fixture(
            self.domain['name'],
            'A',
            fixture=0,
            values={
                'name': '`invalid`label`.%s' % self.domain['name'],
                'records': [
                    '192.0.2.1',
                    '192.0.2.2',
                ]
            }
        )

        url = '/zones/%s/recordsets' % self.domain['id']

        # Ensure it fails with a 400
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_with_name_too_long(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        fixture['name'] = 'x' * 255 + ".%s" % self.domain['name']
        body = fixture
        url = '/zones/%s/recordsets' % self.domain['id']
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_with_name_missing(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        del fixture['name']
        body = fixture
        url = '/zones/%s/recordsets' % self.domain['id']
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_type_is_missing(self):
        # Prepare a RecordSet fixture
        body = self.get_recordset_fixture(
            self.domain['name'],
            'A',
            fixture=0,
            values={
                'name': 'name.%s' % self.domain['name'],
                'records': [
                    '192.0.2.1',
                    '192.0.2.2',
                ]
            }
        )

        del body['type']

        url = '/zones/%s/recordsets' % self.domain['id']

        # Ensure it fails with a 400
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_with_invalid_type(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        fixture['type'] = "ABC"
        body = fixture
        url = '/zones/%s/recordsets' % self.domain['id']
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_description_too_long(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        fixture['description'] = "x" * 161
        body = fixture
        url = '/zones/%s/recordsets' % self.domain['id']
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_with_negative_ttl(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        fixture['ttl'] = -1
        body = fixture
        url = '/zones/%s/recordsets' % self.domain['id']
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_with_zero_ttl(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        fixture['ttl'] = 0
        body = fixture
        url = '/zones/%s/recordsets' % self.domain['id']
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_with_ttl_greater_than_max(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        fixture['ttl'] = 2147483648
        body = fixture
        url = '/zones/%s/recordsets' % self.domain['id']
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_with_invalid_ttl(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        fixture['ttl'] = ">?!?"
        body = fixture
        url = '/zones/%s/recordsets' % self.domain['id']
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    def test_create_recordset_invalid_id(self):
        self._assert_invalid_uuid(self.client.post, '/zones/%s/recordsets')

    def test_create_recordset_validation(self):
        # NOTE: The schemas should be tested separatly to the API. So we
        #       don't need to test every variation via the API itself.
        # Fetch a fixture
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)

        url = '/zones/%s/recordsets' % self.domain['id']

        # Add a junk field to the body
        fixture['junk'] = 'Junk Field'
        body = fixture

        # Ensure it fails with a 400
        self._assert_exception(
            'invalid_object', 400, self.client.post_json, url, body)

    @patch.object(central_service.Service, 'create_recordset',
                  side_effect=messaging.MessagingTimeout())
    def test_create_recordset_timeout(self, _):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)

        body = fixture

        url = '/zones/%s/recordsets' % self.domain['id']

        self._assert_exception('timeout', 504, self.client.post_json, url,
                               body)

    @patch.object(central_service.Service, 'create_recordset',
                  side_effect=exceptions.DuplicateRecordSet())
    def test_create_recordset_duplicate(self, _):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)

        body = fixture

        url = '/zones/%s/recordsets' % self.domain['id']

        self._assert_exception('duplicate_recordset', 409,
                               self.client.post_json, url, body)

    def test_create_recordset_invalid_domain(self):
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)

        body = fixture

        url = '/zones/ba751950-6193-11e3-949a-0800200c9a66/recordsets'

        self._assert_exception('domain_not_found', 404, self.client.post_json,
                               url, body)

    def test_recordsets_invalid_url(self):
        url = '/zones/recordsets'
        self._assert_exception('not_found', 404, self.client.get, url)
        self._assert_exception('not_found', 404, self.client.post_json, url)

        # Pecan returns a 405 for Patch and delete operations
        response = self.client.patch_json(url, status=405)
        self.assertEqual(405, response.status_int)

        response = self.client.delete(url, status=405)
        self.assertEqual(405, response.status_int)

    def test_get_recordsets(self):
        url = '/zones/%s/recordsets' % self.domain['id']

        response = self.client.get(url)

        # 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('recordsets', response.json)
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # We should start with 2 pending recordsets for SOA & NS
        # pending because pool manager is not active
        self.assertEqual(2, len(response.json['recordsets']))
        for recordset in response.json['recordsets']:
            self.assertEqual('CREATE', recordset['action'])
            self.assertEqual('PENDING', recordset['status'])

        soa = self.central_service.find_recordset(
            self.admin_context, criterion={'domain_id': self.domain['id'],
                                           'type': 'SOA'})
        ns = self.central_service.find_recordset(
            self.admin_context, criterion={'domain_id': self.domain['id'],
                                           'type': 'NS'})
        data = [self.create_recordset(self.domain,
                name='x-%s.%s' % (i, self.domain['name']))
                for i in range(0, 10)]
        data.insert(0, ns)
        data.insert(0, soa)

        self._assert_paging(data, url, key='recordsets')

        self._assert_invalid_paging(data, url, key='recordsets')

    def test_get_recordsets_filter(self):
        # Add recordsets for testing
        fixtures = [
            self.get_recordset_fixture(
                self.domain['name'], 'A', fixture=0, values={
                    'records': ['192.0.2.1', '192.0.2.2'],
                    'description': 'Tester1',
                    'ttl': 3600
                }
            ),
            self.get_recordset_fixture(
                self.domain['name'], 'A', fixture=1, values={
                    'records': ['192.0.2.1'],
                    'description': 'Tester2',
                    'ttl': 4000
                }
            )
        ]

        for fixture in fixtures:
            response = self.client.post_json(
                '/zones/%s/recordsets' % self.domain['id'],
                fixture)

        get_urls = [
            # Filter by Name
            '/zones/%s/recordsets?name=%s' % (
                self.domain['id'], fixtures[0]['name']),
            '/zones/%s/recordsets?data=192.0.2.1&name=%s' % (
                self.domain['id'], fixtures[1]['name']),

            # Filter by Type
            '/zones/%s/recordsets?type=A' % self.domain['id'],
            '/zones/%s/recordsets?type=A&name=%s' % (
                self.domain['id'], fixtures[0]['name']),

            # Filter by TTL
            '/zones/%s/recordsets?ttl=3600' % self.domain['id'],

            # Filter by Data
            '/zones/%s/recordsets?data=192.0.2.1' % self.domain['id'],
            '/zones/%s/recordsets?data=192.0.2.2' % self.domain['id'],

            # Filter by Description
            '/zones/%s/recordsets?description=Tester1' % self.domain['id']
        ]

        correct_results = [1, 1, 2, 1, 1, 2, 1, 1]

        for get_url, correct_result in \
                six.moves.zip(get_urls, correct_results):

            response = self.client.get(get_url)

            # Check the headers are what we expect
            self.assertEqual(200, response.status_int)
            self.assertEqual('application/json', response.content_type)

            # Check that the correct number of recordsets match
            self.assertEqual(correct_result, len(response.json['recordsets']))

    def test_get_recordsets_invalid_id(self):
        self._assert_invalid_uuid(self.client.get, '/zones/%s/recordsets')

    @patch.object(central_service.Service, 'get_domain',
                  side_effect=messaging.MessagingTimeout())
    def test_get_recordsets_timeout(self, _):
        url = '/zones/ba751950-6193-11e3-949a-0800200c9a66/recordsets'

        self._assert_exception('timeout', 504, self.client.get, url)

    def test_get_deleted_recordsets(self):
        zone = self.create_domain(fixture=1)
        self.create_recordset(zone)
        url = '/zones/%s/recordsets' % zone['id']

        response = self.client.get(url)

        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)

        # now delete the domain and get the recordsets
        self.client.delete('/zones/%s' % zone['id'], status=202)

        # Simulate the domain having been deleted on the backend
        domain_serial = self.central_service.get_domain(
            self.admin_context, zone['id']).serial
        self.central_service.update_status(
            self.admin_context, zone['id'], "SUCCESS", domain_serial)

        # Check that we get a domain_not_found error
        self._assert_exception('domain_not_found', 404, self.client.get, url)

    def test_get_recordset(self):
        # Create a recordset
        recordset = self.create_recordset(self.domain)

        url = '/zones/%s/recordsets/%s' % (self.domain['id'], recordset['id'])
        response = self.client.get(url)

        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertIsNone(response.json['updated_at'])
        self.assertEqual(recordset['name'], response.json['name'])
        self.assertEqual(recordset['type'], response.json['type'])
        # The action and status are NONE and ACTIVE as there are no records
        self.assertEqual('NONE', response.json['action'])
        self.assertEqual('ACTIVE', response.json['status'])

    def test_get_recordset_invalid_id(self):
        self._assert_invalid_uuid(self.client.get, '/zones/%s/recordsets/%s')

    @patch.object(central_service.Service, 'get_recordset',
                  side_effect=messaging.MessagingTimeout())
    def test_get_recordset_timeout(self, _):
        url = '/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66' % (
            self.domain['id'])

        self._assert_exception('timeout', 504, self.client.get, url,
                               headers={'Accept': 'application/json'})

    @patch.object(central_service.Service, 'get_recordset',
                  side_effect=exceptions.RecordSetNotFound())
    def test_get_recordset_missing(self, _):
        url = '/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66' % (
            self.domain['id'])

        self._assert_exception('recordset_not_found', 404,
                               self.client.get, url,
                               headers={'Accept': 'application/json'})

    def test_update_recordset(self):
        # Create a recordset
        recordset = self.create_recordset(self.domain)

        # Prepare an update body
        body = {'description': 'Tester'}

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        response = self.client.put_json(url, body, status=200)

        # 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('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIsNotNone(response.json['updated_at'])
        self.assertEqual('Tester', response.json['description'])
        # The action and status are NONE and ACTIVE as there are no records
        self.assertEqual('NONE', response.json['action'])
        self.assertEqual('ACTIVE', response.json['status'])

        # Check the zone's status is as expected
        response = self.client.get('/zones/%s' % recordset['domain_id'],
                                   headers=[('Accept', 'application/json')])
        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertEqual('UPDATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

    def test_update_recordset_with_record_create(self):
        # Create a recordset
        recordset = self.create_recordset(self.domain, 'A')

        # The action and status are NONE and ACTIVE as there are no records
        self.assertEqual('NONE', recordset['action'])
        self.assertEqual('ACTIVE', recordset['status'])

        # Prepare an update body
        body = {'description': 'Tester',
                'type': 'A',
                'records': ['192.0.2.1', '192.0.2.2']}

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        response = self.client.put_json(url, body, status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the values returned are what we expect
        self.assertIn('records', response.json)
        self.assertEqual(2, len(response.json['records']))
        self.assertEqual(set(['192.0.2.1', '192.0.2.2']),
                         set(response.json['records']))
        self.assertEqual('UPDATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

        # Check the zone's status is as expected
        response = self.client.get('/zones/%s' % recordset['domain_id'],
                                   headers=[('Accept', 'application/json')])
        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertEqual('UPDATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

    def test_update_recordset_with_record_replace(self):
        # Create a recordset with one record
        recordset = self.create_recordset(self.domain, 'A')
        self.create_record(self.domain, recordset)

        # Prepare an update body
        body = {'description': 'Tester',
                'records': ['192.0.2.201', '192.0.2.202']}

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        response = self.client.put_json(url, body, status=202)

        # Check the headers are what we expect
        self.assertEqual(202, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the values returned are what we expect
        self.assertIn('records', response.json)
        self.assertEqual(2, len(response.json['records']))
        self.assertEqual(set(['192.0.2.201', '192.0.2.202']),
                         set(response.json['records']))

        # Check the zone's status is as expected
        response = self.client.get('/zones/%s' % recordset['domain_id'],
                                   headers=[('Accept', 'application/json')])
        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertEqual('UPDATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

    def test_update_recordset_with_record_clear(self):
        # Create a recordset with one record
        recordset = self.create_recordset(self.domain, 'A')
        self.create_record(self.domain, recordset)

        # Prepare an update body
        body = {'description': 'Tester', 'records': []}

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        response = self.client.put_json(url, body, status=200)

        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        # Check the values returned are what we expect
        self.assertIn('records', response.json)
        self.assertEqual(0, len(response.json['records']))

        # Check the zone's status is as expected
        response = self.client.get('/zones/%s' % recordset['domain_id'],
                                   headers=[('Accept', 'application/json')])
        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertEqual('UPDATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

    def test_update_recordset_invalid_id(self):
        self._assert_invalid_uuid(
            self.client.put_json, '/zones/%s/recordsets/%s')

    def test_update_recordset_validation(self):
        # NOTE: The schemas should be tested separatly to the API. So we
        #       don't need to test every variation via the API itself.
        # Create a zone
        recordset = self.create_recordset(self.domain)

        # Prepare an update body with junk in the wrapper
        body = {'description': 'Tester',
                'records': ['192.3.3.17'],
                'junk': 'Junk Field'}

        # Ensure it fails with a 400
        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])

        self._assert_exception('invalid_object', 400, self.client.put_json,
                               url, body)

        # Prepare an update body with junk in the body
        body = {'description': 'Tester', 'junk': 'Junk Field'}

        # Ensure it fails with a 400
        self._assert_exception('invalid_object', 400, self.client.put_json,
                               url, body)

    @patch.object(central_service.Service, 'get_recordset',
                  side_effect=exceptions.DuplicateRecordSet())
    def test_update_recordset_duplicate(self, _):
        # Prepare an update body
        body = {'description': 'Tester'}

        # Ensure it fails with a 409
        url = ('/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66'
               % (self.domain['id']))

        self._assert_exception('duplicate_recordset', 409,
                               self.client.put_json, url, body)

    @patch.object(central_service.Service, 'get_recordset',
                  side_effect=messaging.MessagingTimeout())
    def test_update_recordset_timeout(self, _):
        # Prepare an update body
        body = {'description': 'Tester'}

        # Ensure it fails with a 504
        url = ('/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66'
               % (self.domain['id']))

        self._assert_exception('timeout', 504, self.client.put_json, url,
                               body)

    @patch.object(central_service.Service, 'get_recordset',
                  side_effect=exceptions.RecordSetNotFound())
    def test_update_recordset_missing(self, _):
        # Prepare an update body
        body = {'description': 'Tester'}

        # Ensure it fails with a 404
        url = ('/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66'
               % (self.domain['id']))

        self._assert_exception('recordset_not_found', 404,
                               self.client.put_json, url, body)

    def test_update_recordset_invalid_ttl(self):
        recordset = self.create_recordset(self.domain)
        body = {'ttl': '>?!@'}
        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        self._assert_exception('invalid_object', 400,
                               self.client.put_json, url, body)

    def test_update_recordset_zero_ttl(self):
        recordset = self.create_recordset(self.domain)
        body = {'ttl': 0}
        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        self._assert_exception('invalid_object', 400,
                               self.client.put_json, url, body)

    def test_update_recordset_negative_ttl(self):
        recordset = self.create_recordset(self.domain)
        body = {'ttl': -1}
        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        self._assert_exception('invalid_object', 400,
                               self.client.put_json, url, body)

    def test_update_recordset_ttl_greater_than_max(self):
        recordset = self.create_recordset(self.domain)
        body = {'ttl': 2174483648}
        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        self._assert_exception('invalid_object', 400,
                               self.client.put_json, url, body)

    def test_update_recordset_description_too_long(self):
        recordset = self.create_recordset(self.domain)
        body = {'description': 'x' * 161}
        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        self._assert_exception('invalid_object', 400,
                               self.client.put_json, url, body)

    def test_delete_recordset(self):
        recordset = self.create_recordset(self.domain)

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        response = self.client.delete(url, status=202)

        self.assertEqual('application/json', response.content_type)
        # Currently recordset does not have a status field. As there are no
        # records, the recordset action/status show up as 'NONE', 'ACTIVE'
        self.assertEqual('NONE', response.json['action'])
        self.assertEqual('ACTIVE', response.json['status'])

        # Check the zone's status is as expected
        response = self.client.get('/zones/%s' % recordset['domain_id'],
                                   headers=[('Accept', 'application/json')])
        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertEqual('UPDATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

    def test_delete_recordset_with_records(self):
        # Create a recordset with one record
        recordset = self.create_recordset(self.domain, 'A')
        self.create_record(self.domain, recordset)

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        response = self.client.delete(url, status=202)

        self.assertEqual('application/json', response.content_type)
        self.assertEqual('DELETE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

        # Check the zone's status is as expected
        response = self.client.get('/zones/%s' % recordset['domain_id'],
                                   headers=[('Accept', 'application/json')])
        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertEqual('UPDATE', response.json['action'])
        self.assertEqual('PENDING', response.json['status'])

    @patch.object(central_service.Service, 'delete_recordset',
                  side_effect=exceptions.RecordSetNotFound())
    def test_delete_recordset_missing(self, _):
        url = ('/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66'
               % (self.domain['id']))

        self._assert_exception('recordset_not_found', 404,
                               self.client.delete, url)

    def test_delete_recordset_invalid_id(self):
        self._assert_invalid_uuid(
            self.client.delete, '/zones/%s/recordsets/%s')

    def test_metadata_exists(self):
        url = '/zones/%s/recordsets' % self.domain['id']

        response = self.client.get(url)

        # Make sure the fields exist
        self.assertIn('metadata', response.json)
        self.assertIn('total_count', response.json['metadata'])

    def test_total_count(self):
        url = '/zones/%s/recordsets' % self.domain['id']

        response = self.client.get(url)

        # The NS and SOA records are there by default
        self.assertEqual(2, response.json['metadata']['total_count'])

        # Create a recordset
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        response = self.client.post_json(
            '/zones/%s/recordsets' % self.domain['id'], fixture)

        response = self.client.get(url)

        # Make sure total_count picked up the change
        self.assertEqual(3, response.json['metadata']['total_count'])

    def test_total_count_filtered_by_data(self):
        # Closes bug 1447325
        url = '/zones/%s/recordsets' % self.domain['id']

        # Create a recordset
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        response = self.client.post_json(
            '/zones/%s/recordsets' % self.domain['id'], fixture)

        response = self.client.get(url)

        # Make sure total_count picked up the change
        self.assertEqual(3, response.json['metadata']['total_count'])

        url = '/zones/%s/recordsets?data=nyan' % self.domain['id']
        response = self.client.get(url)
        self.assertEqual(0, response.json['metadata']['total_count'])

        url = '/zones/%s/recordsets?data=ns1.example.org.' % self.domain['id']
        response = self.client.get(url)
        self.assertEqual(1, response.json['metadata']['total_count'])

        # Test paging
        new_domain = self.create_domain(name='example.net.')
        recordset = self.create_recordset(new_domain, 'A')
        self.create_record(new_domain, recordset, data='nyan')

        recordset = self.create_recordset(new_domain, 'CNAME')
        self.create_record(new_domain, recordset, data='nyan')

        # Even with paging enabled, total_count is still the total number of
        # recordsets matching the "data" filter
        url = '/zones/%s/recordsets?limit=1&data=nyan' % new_domain.id
        response = self.client.get(url)
        self.assertEqual(2, response.json['metadata']['total_count'])

    def test_total_count_pagination(self):
        # Create two recordsets
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        response = self.client.post_json(
            '/zones/%s/recordsets' % self.domain['id'], fixture)

        fixture = self.get_recordset_fixture(self.domain['name'], fixture=1)
        response = self.client.post_json(
            '/zones/%s/recordsets' % self.domain['id'], fixture)

        # Paginate the recordsets to two, there should be four now
        url = '/zones/%s/recordsets?limit=2' % self.domain['id']

        response = self.client.get(url)

        # There are two recordsets returned
        self.assertEqual(2, len(response.json['recordsets']))

        # But there should be four in total (NS/SOA + the created)
        self.assertEqual(4, response.json['metadata']['total_count'])

    # Secondary Zones specific tests
    def test_get_secondary_zone_recordset(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = '*****@*****.**'
        secondary = self.create_domain(**fixture)

        # Create a recordset
        recordset = self.create_recordset(secondary)

        url = '/zones/%s/recordsets/%s' % (secondary['id'], recordset['id'])
        response = self.client.get(url)

        # Check the headers are what we expect
        self.assertEqual(200, response.status_int)
        self.assertEqual('application/json', response.content_type)

        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # Check the values returned are what we expect
        self.assertIn('id', response.json)
        self.assertIn('created_at', response.json)
        self.assertIsNone(response.json['updated_at'])
        self.assertEqual(recordset['name'], response.json['name'])
        self.assertEqual(recordset['type'], response.json['type'])

    def test_get_secondary_zone_recordsets(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = '*****@*****.**'
        secondary = self.create_domain(**fixture)

        url = '/zones/%s/recordsets' % secondary['id']

        response = self.client.get(url)

        # 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('recordsets', response.json)
        self.assertIn('links', response.json)
        self.assertIn('self', response.json['links'])

        # We should start with 2 recordsets for SOA & NS
        self.assertEqual(1, len(response.json['recordsets']))

        soa = self.central_service.find_recordset(
            self.admin_context, criterion={'domain_id': secondary['id'],
                                           'type': 'SOA'})
        data = [self.create_recordset(secondary,
                name='x-%s.%s' % (i, secondary['name']))
                for i in range(0, 10)]
        data.insert(0, soa)

        self._assert_paging(data, url, key='recordsets')

        self._assert_invalid_paging(data, url, key='recordsets')

    def test_create_secondary_zone_recordset(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = '*****@*****.**'
        secondary = self.create_domain(**fixture)

        fixture = self.get_recordset_fixture(secondary['name'], fixture=0)

        url = '/zones/%s/recordsets' % secondary['id']
        self._assert_exception('forbidden', 403, self.client.post_json, url,
                               fixture)

    def test_update_secondary_zone_recordset(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = '*****@*****.**'
        secondary = self.create_domain(**fixture)

        # Set the context so that we can create a RRSet
        recordset = self.create_recordset(secondary)

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])

        self._assert_exception('forbidden', 403, self.client.put_json, url,
                               {'ttl': 100})

    def test_delete_secondary_zone_recordset(self):
        fixture = self.get_domain_fixture('SECONDARY', 1)
        fixture['email'] = '*****@*****.**'
        secondary = self.create_domain(**fixture)

        # Set the context so that we can create a RRSet
        recordset = self.create_recordset(secondary)

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])

        self._assert_exception('forbidden', 403, self.client.delete, url)

    def test_no_create_rs_deleting_zone(self):
        # Prepare a create
        fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
        body = fixture

        self.client.delete('/zones/%s' % self.domain['id'], status=202)
        self._assert_exception('bad_request', 400, self.client.post_json,
                               '/zones/%s/recordsets' % self.domain['id'],
                               body)

    def test_no_update_rs_deleting_zone(self):
        # Create a recordset
        recordset = self.create_recordset(self.domain)

        # Prepare an update body
        body = {'description': 'Tester'}
        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])
        self.client.delete('/zones/%s' % self.domain['id'], status=202)
        self._assert_exception('bad_request', 400, self.client.put_json, url,
                               body)

    def test_no_delete_rs_deleting_zone(self):
        # Create a recordset
        recordset = self.create_recordset(self.domain)

        url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
                                           recordset['id'])

        self.client.delete('/zones/%s' % self.domain['id'], status=202)
        self._assert_exception('bad_request', 400, self.client.delete, url)

    def test_invalid_recordset_filter(self):
        invalid_url = '/zones/%s/recordsets?action=NONE' % self.domain['id']
        self._assert_exception(
            'bad_request', 400, self.client.get, invalid_url)
Beispiel #26
0
class ApiV1zonesTest(ApiV1Test):
    def test_get_zone_schema(self):
        response = self.get('schemas/domain')
        self.assertIn('description', response.json)
        self.assertIn('links', response.json)
        self.assertIn('title', response.json)
        self.assertIn('id', response.json)
        self.assertIn('additionalProperties', response.json)
        self.assertIn('properties', response.json)
        self.assertIn('description', response.json['properties'])
        self.assertIn('created_at', response.json['properties'])
        self.assertIn('updated_at', response.json['properties'])
        self.assertIn('name', response.json['properties'])
        self.assertIn('email', response.json['properties'])
        self.assertIn('ttl', response.json['properties'])
        self.assertIn('serial', response.json['properties'])

    def test_get_zones_schema(self):
        response = self.get('schemas/domains')
        self.assertIn('description', response.json)
        self.assertIn('additionalProperties', response.json)
        self.assertIn('properties', response.json)
        self.assertIn('title', response.json)
        self.assertIn('id', response.json)

    def test_create_zone(self):
        # Create a zone
        fixture = self.get_zone_fixture(0)

        # V1 doesn't have these
        del fixture['type']

        response = self.post('domains', data=fixture)

        self.assertIn('id', response.json)
        self.assertIn('name', response.json)
        self.assertEqual(response.json['name'], fixture['name'])

    def test_create_zone_junk(self):
        # Create a zone
        fixture = self.get_zone_fixture(0)

        # Add a junk property
        fixture['junk'] = 'Junk Field'

        # Ensure it fails with a 400
        self.post('domains', data=fixture, status_code=400)

    @patch.object(central_service.Service, 'create_zone',
                  side_effect=messaging.MessagingTimeout())
    def test_create_zone_timeout(self, _):
        # Create a zone
        fixture = self.get_zone_fixture(0)

        # V1 doesn't have these
        del fixture['type']

        self.post('domains', data=fixture, status_code=504)

    @patch.object(central_service.Service, 'create_zone',
                  side_effect=exceptions.DuplicateZone())
    def test_create_zone_duplicate(self, _):
        # Create a zone
        fixture = self.get_zone_fixture(0)

        # V1 doesn't have these
        del fixture['type']

        self.post('domains', data=fixture, status_code=409)

    def test_create_zone_null_ttl(self):
        # Create a zone
        fixture = self.get_zone_fixture(0)
        fixture['ttl'] = None
        self.post('domains', data=fixture, status_code=400)

    def test_create_zone_negative_ttl(self):
        # Create a zone
        fixture = self.get_zone_fixture(0)
        fixture['ttl'] = -1
        self.post('domains', data=fixture, status_code=400)

    def test_create_zone_zero_ttl(self):
        # Create a zone
        fixture = self.get_zone_fixture(0)
        fixture['ttl'] = 0
        self.post('domains', data=fixture, status_code=400)

    def test_create_zone_invalid_ttl(self):
        # Create a zone
        fixture = self.get_zone_fixture(0)
        fixture['ttl'] = "$?>&"
        self.post('domains', data=fixture, status_code=400)

    def test_create_zone_ttl_greater_than_max(self):
        fixture = self.get_zone_fixture(0)
        fixture['ttl'] = 2147483648
        self.post('domains', data=fixture, status_code=400)

    def test_create_zone_utf_description(self):
        # Create a zone
        fixture = self.get_zone_fixture(0)

        # V1 doesn't have type
        del fixture['type']

        # Give it a UTF-8 filled description
        fixture['description'] = "utf-8:2H₂+O₂⇌2H₂O,R=4.7kΩ,⌀200mm∮E⋅da=Q,n" \
                                 ",∑f(i)=∏g(i),∀x∈ℝ:⌈x⌉"
        # Create the zone, ensuring it succeeds, thus UTF-8 is supported
        self.post('domains', data=fixture)

    def test_create_zone_description_too_long(self):
        # Create a zone
        fixture = self.get_zone_fixture(0)
        fixture['description'] = "x" * 161

        # Create the zone, ensuring it fails with a 400
        self.post('domains', data=fixture, status_code=400)

    def test_create_zone_with_unwanted_attributes(self):

        zone_id = "2d1d1d1d-1324-4a80-aa32-1f69a91bf2c8"
        created_at = datetime.datetime(2014, 6, 22, 21, 50, 0)
        updated_at = datetime.datetime(2014, 6, 22, 21, 50, 0)
        serial = 1234567

        # Create a zone
        fixture = self.get_zone_fixture(0)
        fixture['id'] = zone_id
        fixture['created_at'] = created_at
        fixture['updated_at'] = updated_at
        fixture['serial'] = serial

        self.post('domains', data=fixture, status_code=400)

    def test_create_invalid_name(self):
        # Prepare a zone
        fixture = self.get_zone_fixture(0)

        invalid_names = [
            'org',
            'example.org',
            'example.321',
        ]

        for invalid_name in invalid_names:
            fixture['name'] = invalid_name

            # Create a record
            response = self.post('domains', data=fixture, status_code=400)

            self.assertNotIn('id', response.json)

    def test_create_zone_name_too_long(self):
        fixture = self.get_zone_fixture(0)

        long_name = 'a' * 255 + ".org."
        fixture['name'] = long_name

        response = self.post('domains', data=fixture, status_code=400)

        self.assertNotIn('id', response.json)

    def test_create_zone_name_is_not_present(self):
        fixture = self.get_zone_fixture(0)
        del fixture['name']
        self.post('domains', data=fixture, status_code=400)

    def test_create_invalid_email(self):
        # Prepare a zone
        fixture = self.get_zone_fixture(0)

        invalid_emails = [
            'org',
            'example.org',
            'bla.example.org',
            'org.',
            'example.org.',
            'bla.example.org.',
            'bla.example.org.',
        ]

        for invalid_email in invalid_emails:
            fixture['email'] = invalid_email

            # Create a record
            response = self.post('domains', data=fixture, status_code=400)

            self.assertNotIn('id', response.json)

    def test_create_zone_email_too_long(self):
        fixture = self.get_zone_fixture(0)

        long_email = 'a' * 255 + "@org.com"
        fixture['email'] = long_email

        response = self.post('domains', data=fixture, status_code=400)

        self.assertNotIn('id', response.json)

    def test_create_zone_email_not_present(self):
        fixture = self.get_zone_fixture(0)
        del fixture['email']
        self.post('domains', data=fixture, status_code=400)

    def test_create_zone_twice(self):
        self.create_zone()
        with testtools.ExpectedException(exceptions.DuplicateZone):
            self.create_zone()

    def test_create_zone_pending_deletion(self):
        zone = self.create_zone()
        self.delete('domains/%s' % zone['id'])
        with testtools.ExpectedException(exceptions.DuplicateZone):
            self.create_zone()

    def test_get_zones(self):
        response = self.get('domains')

        self.assertIn('domains', response.json)
        self.assertEqual(0, len(response.json['domains']))

        # Create a zone
        self.create_zone()

        response = self.get('domains')

        self.assertIn('domains', response.json)
        self.assertEqual(1, len(response.json['domains']))

        # Create a second zone
        self.create_zone(fixture=1)

        response = self.get('domains')

        self.assertIn('domains', response.json)
        self.assertEqual(2, len(response.json['domains']))

    def test_get_zone_servers(self):
        # Create a zone
        zone = self.create_zone()
        response = self.get('domains/%s/servers' % zone['id'])
        # Verify length of zone servers
        self.assertEqual(1, len(response.json['servers']))

    @patch.object(central_service.Service, 'find_zones',
                  side_effect=messaging.MessagingTimeout())
    def test_get_zones_timeout(self, _):
        self.get('domains', status_code=504)

    def test_get_zone(self):
        # Create a zone
        zone = self.create_zone()

        response = self.get('domains/%s' % zone['id'])

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], zone['id'])

    @patch.object(central_service.Service, 'find_zone',
                  side_effect=messaging.MessagingTimeout())
    def test_get_zone_timeout(self, _):
        # Create a zone
        zone = self.create_zone()

        self.get('domains/%s' % zone['id'], status_code=504)

    def test_get_zone_missing(self):
        self.get('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
                 status_code=404)

    def test_get_zone_invalid_id(self):
        # The letter "G" is not valid in a UUID
        self.get('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
                 status_code=404)

        self.get('domains/2fdadfb1cf964259ac6bbb7b6d2ff980', status_code=404)

    def test_update_zone(self):
        # Create a zone
        zone = self.create_zone()

        data = {'email': 'prefix-%s' % zone['email']}

        response = self.put('domains/%s' % zone['id'], data=data)

        self.assertIn('id', response.json)
        self.assertEqual(response.json['id'], zone['id'])

        self.assertIn('email', response.json)
        self.assertEqual('prefix-%s' % zone['email'], response.json['email'])

    def test_update_zone_junk(self):
        # Create a zone
        zone = self.create_zone()

        data = {'email': 'prefix-%s' % zone['email'], 'junk': 'Junk Field'}

        self.put('domains/%s' % zone['id'], data=data, status_code=400)

    def test_update_zone_name_fail(self):
        # Create a zone
        zone = self.create_zone()

        data = {'name': 'renamed.com.'}

        self.put('domains/%s' % zone['id'], data=data, status_code=400)

    def test_update_zone_null_ttl(self):
        # Create a zone
        zone = self.create_zone()

        data = {'ttl': None}

        self.put('domains/%s' % zone['id'], data=data, status_code=400)

    def test_update_zone_negative_ttl(self):
        # Create a zone
        zone = self.create_zone()

        data = {'ttl': -1}

        self.put('domains/%s' % zone['id'], data=data, status_code=400)

    def test_update_zone_zero_ttl(self):
        # Create a zone
        zone = self.create_zone()

        data = {'ttl': 0}

        self.put('domains/%s' % zone['id'], data=data, status_code=400)

    @patch.object(central_service.Service, 'update_zone',
                  side_effect=messaging.MessagingTimeout())
    def test_update_zone_timeout(self, _):
        # Create a zone
        zone = self.create_zone()

        data = {'email': 'prefix-%s' % zone['email']}

        self.put('domains/%s' % zone['id'], data=data, status_code=504)

    @patch.object(central_service.Service, 'update_zone',
                  side_effect=exceptions.DuplicateZone())
    def test_update_zone_duplicate(self, _):
        # Create a zone
        zone = self.create_zone()

        data = {'email': 'prefix-%s' % zone['email']}

        self.put('domains/%s' % zone['id'], data=data, status_code=409)

    def test_update_zone_missing(self):
        data = {'email': '*****@*****.**'}

        self.put('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980', data=data,
                 status_code=404)

    def test_update_zone_invalid_id(self):
        data = {'email': '*****@*****.**'}

        # The letter "G" is not valid in a UUID
        self.put('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG', data=data,
                 status_code=404)

        self.put('domains/2fdadfb1cf964259ac6bbb7b6d2ff980', data=data,
                 status_code=404)

    def test_update_zone_ttl_greter_than_max(self):
        # Create a zone
        zone = self.create_zone()

        data = {'ttl': 2147483648}

        self.put('domains/%s' % zone['id'], data=data, status_code=400)

    def test_update_zone_invalid_email(self):
        # Create a zone
        zone = self.create_zone()

        invalid_emails = [
            'org',
            'example.org',
            'bla.example.org',
            'org.',
            'example.org.',
            'bla.example.org.',
            'bla.example.org.',
            'a' * 255 + "@com",
            ''
        ]

        for invalid_email in invalid_emails:
            data = {'email': invalid_email}
            self.put('domains/%s' % zone['id'], data=data, status_code=400)

    def test_update_zone_description_too_long(self):
        # Create a zone
        zone = self.create_zone()

        invalid_des = 'a' * 165

        data = {'description': invalid_des}
        self.put('domains/%s' % zone['id'], data=data, status_code=400)

    def test_update_zone_in_pending_deletion(self):
        zone = self.create_zone()
        self.delete('domains/%s' % zone['id'])
        self.put('domains/%s' % zone['id'], data={}, status_code=404)

    def test_delete_zone(self):
        # Create a zone
        zone = self.create_zone()

        self.delete('domains/%s' % zone['id'])

        # Simulate the zone having been deleted on the backend
        zone_serial = self.central_service.get_zone(
            self.admin_context, zone['id']).serial
        self.central_service.update_status(
            self.admin_context, zone['id'], "SUCCESS", zone_serial)

        # Ensure we can no longer fetch the zone
        self.get('domains/%s' % zone['id'], status_code=404)

    def test_zone_in_pending_deletion(self):
        zone1 = self.create_zone()
        self.create_zone(fixture=1)
        response = self.get('domains')
        self.assertEqual(2, len(response.json['domains']))

        # Delete zone1
        self.delete('domains/%s' % zone1['id'])

        # Ensure we can no longer list nor fetch the deleted zone
        response = self.get('domains')
        self.assertEqual(1, len(response.json['domains']))

        self.get('domains/%s' % zone1['id'], status_code=404)

    @patch.object(central_service.Service, 'delete_zone',
                  side_effect=messaging.MessagingTimeout())
    def test_delete_zone_timeout(self, _):
        # Create a zone
        zone = self.create_zone()

        self.delete('domains/%s' % zone['id'], status_code=504)

    def test_delete_zone_missing(self):
        self.delete('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
                    status_code=404)

    def test_delete_zone_invalid_id(self):
        # The letter "G" is not valid in a UUID
        self.delete('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
                    status_code=404)

        self.delete('domains/2fdadfb1cf964259ac6bbb7b6d2ff980',
                    status_code=404)

    def test_get_secondary_missing(self):
        fixture = self.get_zone_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        zone = self.create_zone(**fixture)

        self.get('domains/%s' % zone.id, status_code=404)

    def test_update_secondary_missing(self):
        fixture = self.get_zone_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        zone = self.create_zone(**fixture)

        self.put('domains/%s' % zone.id, {}, status_code=404)

    def test_delete_secondary_missing(self):
        fixture = self.get_zone_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        zone = self.create_zone(**fixture)
        self.delete('domains/%s' % zone.id, status_code=404)

    def test_get_zone_servers_from_secondary(self):
        fixture = self.get_zone_fixture('SECONDARY', 0)
        fixture['email'] = cfg.CONF['service:central'].managed_resource_email

        zone = self.create_zone(**fixture)
        self.get('domains/%s/servers' % zone.id, status_code=404)