designate-tempest-plugin/designate_tempest_plugin/tests/scenario/v2/test_zones.py

259 lines
11 KiB
Python

# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
import math
from oslo_log import log as logging
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
import testtools
from designate_tempest_plugin import data_utils as dns_data_utils
from designate_tempest_plugin.tests import base
from designate_tempest_plugin.common import constants as const
from designate_tempest_plugin.common import waiters
from designate_tempest_plugin.services.dns.query.query_client \
import SingleQueryClient
CONF = config.CONF
CONF = config.CONF
LOG = logging.getLogger(__name__)
class ZonesTest(base.BaseDnsV2Test):
credentials = ["primary", "admin", "system_admin"]
@classmethod
def setup_clients(cls):
super(ZonesTest, cls).setup_clients()
if CONF.enforce_scope.designate:
cls.admin_tld_client = cls.os_system_admin.dns_v2.TldClient()
cls.rec_client = cls.os_system_admin.dns_v2.RecordsetClient()
else:
cls.admin_tld_client = cls.os_admin.dns_v2.TldClient()
cls.rec_client = cls.os_admin.dns_v2.RecordsetClient()
cls.client = cls.os_primary.dns_v2.ZonesClient()
cls.primary_client = cls.os_primary.dns_v2.BlacklistsClient()
@classmethod
def resource_setup(cls):
super(ZonesTest, cls).resource_setup()
# Make sure we have an allowed TLD available
tld_name = dns_data_utils.rand_zone_name(name="ZonesTest")
cls.tld_name = f".{tld_name}"
cls.class_tld = cls.admin_tld_client.create_tld(tld_name=tld_name[:-1])
@classmethod
def resource_cleanup(cls):
cls.admin_tld_client.delete_tld(cls.class_tld[1]['id'])
super(ZonesTest, cls).resource_cleanup()
@decorators.attr(type='smoke')
@decorators.attr(type='slow')
@decorators.idempotent_id('d0648f53-4114-45bd-8792-462a82f69d32')
def test_create_and_delete_zone(self):
LOG.info('Create a zone')
zone_name = dns_data_utils.rand_zone_name(
name="create_and_delete_zone", suffix=self.tld_name)
zone = self.client.create_zone(name=zone_name)[1]
self.addCleanup(self.wait_zone_delete, self.client, zone['id'],
ignore_errors=lib_exc.NotFound)
LOG.info('Ensure we respond with CREATE+PENDING')
self.assertEqual(const.CREATE, zone['action'])
self.assertEqual(const.PENDING, zone['status'])
waiters.wait_for_zone_status(
self.client, zone['id'], const.ACTIVE)
LOG.info('Re-Fetch the zone')
zone = self.client.show_zone(zone['id'])[1]
LOG.info('Ensure we respond with NONE+ACTIVE')
self.assertEqual(const.NONE, zone['action'])
self.assertEqual(const.ACTIVE, zone['status'])
LOG.info('Delete the zone')
zone = self.client.delete_zone(zone['id'])[1]
LOG.info('Ensure we respond with DELETE+PENDING')
self.assertEqual(const.DELETE, zone['action'])
self.assertEqual(const.PENDING, zone['status'])
waiters.wait_for_zone_404(self.client, zone['id'])
@decorators.attr(type='slow')
@decorators.idempotent_id('cabd6334-ba37-11ec-9d8c-201e8823901f')
def test_create_and_update_zone(self):
LOG.info('Create a zone and wait until it becomes ACTIVE')
orig_ttl = 666
orig_description = 'test_create_and_update_zone: org description'
zone_name = dns_data_utils.rand_zone_name(
name="create_and_update_zone", suffix=self.tld_name)
zone = self.client.create_zone(
name=zone_name,
ttl=orig_ttl, description=orig_description,
wait_until=const.ACTIVE)[1]
self.addCleanup(self.wait_zone_delete, self.client, zone['id'],
ignore_errors=lib_exc.NotFound)
LOG.info("Update zone's: TTL and Description, wait until ACTIVE")
updated_ttl = 777
updated_description = dns_data_utils.rand_string(20)
self.client.update_zone(
zone['id'], ttl=updated_ttl, description=updated_description,
wait_until=const.ACTIVE)
LOG.info('Re-Fetch/Show the zone')
show_zone = self.client.show_zone(zone['id'])[1]
LOG.info('Ensure that the Description and TLL has been updated')
self.assertEqual(
updated_ttl, show_zone['ttl'],
'Failed, actual TTL value:{} is not as expected:{} after '
'the update)'.format(show_zone['ttl'], updated_ttl))
self.assertEqual(
updated_description, show_zone['description'],
'Failed, actual Description:{} is not as expected:{} after '
'the update)'.format(show_zone['description'], orig_description))
@decorators.attr(type='slow')
@decorators.idempotent_id('c9838adf-14dc-4097-9130-e5cea3727abb')
def test_delete_zone_pending_create(self):
LOG.info('Create a zone')
zone_name = dns_data_utils.rand_zone_name(
name="delete_zone_pending_create", suffix=self.tld_name)
zone = self.client.create_zone(name=zone_name)[1]
self.addCleanup(self.wait_zone_delete, self.client, zone['id'],
ignore_errors=lib_exc.NotFound)
# NOTE(kiall): This is certainly a little racey, it's entirely
# possible the zone will become active before we delete
# it. Worst case, that means we get an unexpected pass.
# Theres not a huge amount we can do, given this is
# black-box testing.
LOG.info('Delete the zone while it is still pending')
zone = self.client.delete_zone(zone['id'])[1]
LOG.info('Ensure we respond with DELETE+PENDING')
self.assertEqual(const.DELETE, zone['action'])
self.assertEqual(const.PENDING, zone['status'])
waiters.wait_for_zone_404(self.client, zone['id'])
@decorators.attr(type='slow')
@decorators.idempotent_id('ad8d1f5b-da66-46a0-bbee-14dc84a5d791')
@testtools.skipUnless(
config.CONF.dns.nameservers,
"Config option dns.nameservers is missing or empty")
def test_zone_create_propagates_to_nameservers(self):
LOG.info('Create a zone')
zone_name = dns_data_utils.rand_zone_name(
name="zone_create_propagates", suffix=self.tld_name)
zone = self.client.create_zone(name=zone_name)[1]
self.addCleanup(self.wait_zone_delete, self.client, zone['id'])
waiters.wait_for_zone_status(self.client, zone['id'], const.ACTIVE)
waiters.wait_for_query(self.query_client, zone['name'], const.SOA)
@decorators.attr(type='slow')
@decorators.idempotent_id('d13d3095-c78f-4aae-8fe3-a74ccc335c84')
@testtools.skipUnless(
config.CONF.dns.nameservers,
"Config option dns.nameservers is missing or empty")
def test_zone_delete_propagates_to_nameservers(self):
LOG.info('Create a zone')
zone_name = dns_data_utils.rand_zone_name(
name="zone_delete_propagates", suffix=self.tld_name)
zone = self.client.create_zone(name=zone_name)[1]
self.addCleanup(self.wait_zone_delete, self.client, zone['id'],
ignore_errors=lib_exc.NotFound)
waiters.wait_for_zone_status(self.client, zone['id'], const.ACTIVE)
waiters.wait_for_query(self.query_client, zone['name'], const.SOA)
LOG.info('Delete the zone')
self.client.delete_zone(zone['id'])
waiters.wait_for_zone_404(self.client, zone['id'])
waiters.wait_for_query(self.query_client, zone['name'], const.SOA,
found=False)
@decorators.attr(type='slow')
@decorators.idempotent_id('ff9b9fc4-85b4-11ec-bcf5-201e8823901f')
@testtools.skipUnless(
config.CONF.dns.nameservers,
"Config option dns.nameservers is missing or empty")
def test_notify_msg_sent_to_nameservers(self):
# Test will only run when the SOA record Refresh is close to one hour,
# otherwise skipped.
# This implies that the only reason "A" record was propagated is as a
# result of successfully sent NOTIFY message.
LOG.info('Create a zone, wait until ACTIVE and get the Serial'
' and SOA Refresh values')
zone_name = dns_data_utils.rand_zone_name(
name="test_notify_msg_sent_to_nameservers", suffix=self.tld_name)
zone = self.client.create_zone(name=zone_name, wait_until='ACTIVE')[1]
org_serial = zone['serial']
self.addCleanup(self.wait_zone_delete, self.client, zone['id'])
try:
soa = [
rec['records'] for rec in self.rec_client.list_recordset(
zone['id'], headers=self.all_projects_header)[1][
'recordsets'] if rec['type'] == 'SOA'][0]
refresh = int(soa[0].split(' ')[3])
if math.isclose(3600, refresh, rel_tol=0.1) is False:
raise self.skipException(
'Test is skipped, actual SOA REFRESH is:{} unlike test'
' prerequisites that requires a value close to 3600'
' (one hour)'.format(refresh))
except Exception as e:
raise self.skipException(
'Test is skipped, something went wrong on getting SOA REFRESH'
' value, the error was:{}'.format(e))
LOG.info("Update Zone's TTL, wait until ACTIVE and"
" ensure Zone's Serial has changed")
self.client.update_zone(
zone['id'], ttl=dns_data_utils.rand_ttl(), wait_until='ACTIVE')
new_serial = self.client.show_zone(zone['id'])[1]['serial']
self.assertNotEqual(
new_serial, org_serial,
"Failed, expected behaviour is that the Designate DNS changes the"
" Serial after updating Zone's TTL value")
waiters.wait_for_query(self.query_client, zone['name'], "SOA")
LOG.info('Per Nameserver "dig" for a SOA record until either:'
' updated Serial is detected or build timeout has reached')
for ns in config.CONF.dns.nameservers:
start = time.time()
while True:
ns_obj = SingleQueryClient(ns, config.CONF.dns.query_timeout)
ns_soa_record = ns_obj.query(zone['name'], rdatatype='SOA')
if str(new_serial) in str(ns_soa_record):
return
if time.time() - start >= config.CONF.dns.build_timeout:
raise lib_exc.TimeoutException(
'Failed, expected Serial:{} for a Zone was not'
' detected on Nameserver:{} within a timeout of:{}'
' seconds.'.format(
new_serial, ns, config.CONF.dns.build_timeout))