Add designate-manage pool scenario tests

So far we did not test designate-manage pool commands with real end-user
scenarios.

This patch adds designate-manage pool scenario tests.

Change-Id: Iaf848350a502b8f1697b495ee645a09a00974f24
This commit is contained in:
Omer
2024-05-09 00:21:51 +02:00
parent 8c57aa24da
commit c1f894bbff
9 changed files with 454 additions and 1 deletions

View File

@ -16,6 +16,18 @@
nodeset: openstack-single-node-jammy
override-checkout: stable/2023.2
- job:
name: designate-multipool
parent: designate-base
nodeset: openstack-single-node-jammy
vars:
devstack_local_conf:
test-config:
"$TEMPEST_CONFIG":
dns_feature_enabled:
test_multipool_with_delete_opt: True
tempest_test_regex: ^designate_tempest_plugin.tests.scenario.v2.test_designate_multipool
- project:
templates:
- designate-devstack-jobs
@ -28,4 +40,5 @@
- designate-bind9-stable-2024-2
- designate-bind9-stable-2024-1
- designate-bind9-stable-2023-2
- designate-multipool
- neutron-tempest-plugin-designate-scenario

View File

@ -98,6 +98,11 @@ DnsFeatureGroup = [
'the new keystone default roles? This configuration '
'value should be same as designate.conf: '
'[oslo_policy].enforce_new_defaults option.'),
cfg.BoolOpt('test_multipool_with_delete_opt',
default=False,
help="Is multipool feature being tested with --delete option?"
"If it is, it might delete pools that were created in "
"other tests."),
]
# Extending this enforce_scope group defined in tempest

View File

@ -14,6 +14,7 @@
from operator import itemgetter
import testtools
from oslo_log import log as logging
from tempest import config
from tempest.lib import decorators
@ -51,6 +52,10 @@ class PoolAdminTest(BasePoolTest):
cls.admin_client = cls.os_admin.dns_v2.PoolClient()
@decorators.idempotent_id('69257f7c-b3d5-4e1b-998e-0677ad12f125')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_create_pool(self):
pool_data = {
"name": "Example Pool",
@ -79,6 +84,10 @@ class PoolAdminTest(BasePoolTest):
project_id=pool_data["project_id"])
@decorators.idempotent_id('e80eb70a-8ee5-40eb-b06e-599597a8ab7e')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_show_pool(self):
LOG.info('Create a pool')
_, pool = self.admin_client.create_pool(project_id="1")
@ -106,6 +115,10 @@ class PoolAdminTest(BasePoolTest):
headers=self.all_projects_header)
@decorators.idempotent_id('d8c4c377-5d88-452d-a4d2-c004d72e1abe')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_delete_pool(self):
LOG.info('Create a pool')
_, pool = self.admin_client.create_pool(project_id="1")
@ -128,6 +141,10 @@ class PoolAdminTest(BasePoolTest):
'PoolClient', 'delete_pool', expected_allowed, False, pool['id'])
@decorators.idempotent_id('77c85b40-83b2-4c17-9fbf-e6d516cfce90')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_list_pools(self):
LOG.info('Create a pool')
_, pool = self.admin_client.create_pool(project_id="1")
@ -150,6 +167,10 @@ class PoolAdminTest(BasePoolTest):
headers=self.all_projects_header)
@decorators.idempotent_id('fdcc84ce-af65-4af6-a5fc-6c50acbea0f0')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_update_pool(self):
LOG.info('Create a pool')
_, pool = self.admin_client.create_pool(project_id="1")
@ -302,6 +323,10 @@ class TestPoolAdminNegative(BasePoolTest):
ns_records=[{"hostname": "ns1.example.org.", "priority": -1}])
@decorators.idempotent_id('cc378e4c-ac05-11eb-ae06-74e5f9e2a801')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
# Note: Update pool API is deprecated for removal.
def test_update_pool_with_invalid_name(self):
LOG.info('Create a pool')
@ -317,6 +342,10 @@ class TestPoolAdminNegative(BasePoolTest):
headers=self.all_projects_header, extra_headers=True)
@decorators.idempotent_id('2e496596-ac07-11eb-ae06-74e5f9e2a801')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_update_pool_with_invalid_hostname_in_ns_records(self):
# Note: Update pool API is deprecated for removal.
LOG.info('Create a pool')
@ -332,6 +361,10 @@ class TestPoolAdminNegative(BasePoolTest):
headers=self.all_projects_header, extra_headers=True)
@decorators.idempotent_id('3e934624-ac07-11eb-ae06-74e5f9e2a801')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_update_pool_with_invalid_priority_in_ns_records(self):
# Note: Update pool API is deprecated for removal.
LOG.info('Create a pool')

View File

@ -11,7 +11,7 @@
# 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 testtools
from oslo_log import log as logging
from tempest import config
from tempest.lib.common.utils import data_utils
@ -300,6 +300,10 @@ class TsigkeyAdminTest(BaseTsigkeyTest):
0, len(listed_tsigkeys), 'Failed, no tsigkey should be listed')
@decorators.idempotent_id('e8bcf80a-d8b4-11eb-b95a-74e5f9e2a801')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_list_tsigkey_filter_by_scope(self):
LOG.info('Create tsigkey for a pool')
@ -348,6 +352,10 @@ class TsigkeyAdminTest(BaseTsigkeyTest):
'Failed, no tsigkey is expected to be listed')
@decorators.idempotent_id('794554f0-d8b8-11eb-b95a-74e5f9e2a801')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
def test_list_tsigkey_filter_by_algorithm(self):
LOG.info('Create tsigkey for a pool')
@ -658,6 +666,10 @@ class TestTsigkeyInvalidIdAdmin(BaseTsigkeyTest):
tsigkey_data['secret'], tsigkey_data['scope'])
@decorators.idempotent_id('0dfbc2f8-d8bb-11eb-b95a-74e5f9e2a801')
@testtools.skipIf(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
@decorators.skip_because(bug="1934120")
def test_create_tsigkey_for_pool_with_scope_zone(self):
pool = self.pool_admin_client.create_pool()[1]

View File

@ -0,0 +1,20 @@
# Copyright (C) 2024 Red Hat
#
# 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 os
# The idea here is that anything that needs resources can do:
#
# from designate.tests import resources
# my_resource_path = os.path.join(resources.path, my_resource_folder)
path = os.path.dirname(os.path.realpath(__file__))

View File

@ -0,0 +1,59 @@
---
- name: pool-2
id: cf2e8eab-76cd-4162-bf76-8aeee3556de0
description: BIND9 Pool-2
attributes:
internal: true
ns_records:
- hostname: ns1-1.example.org.
priority: 1
- hostname: ns1-2.example.org.
priority: 2
nameservers:
- host: 192.0.2.2
port: 1053
# - host: 192.0.2.3
# port: 53
targets:
- type: bind9
description: BIND9 Server 1
masters:
- host: 192.0.2.1
port: 5354
options:
host: 192.0.2.2
port: 1053
rndc_host: 192.0.2.2
rndc_port: 1953
rndc_key_file: /etc/designate/rndc.key
tsigkey_name: multiple-pools-pool-2
- name: default
description: Default BIND9 Pool
attributes: {}
ns_records:
- hostname: ns1-1.example.org.
priority: 1
- hostname: ns1-2.example.org.
priority: 2
nameservers:
- host: 192.0.2.2
port: 53
# - host: 192.0.2.3
# port: 53
targets:
- type: bind9
description: Default BIND9 Server
masters:
- host: 192.0.2.1
port: 5354
options:
host: 192.0.2.2
port: 53
rndc_host: 192.0.2.2
rndc_port: 953
rndc_key_file: /etc/designate/rndc.key

View File

@ -0,0 +1,61 @@
---
- name: other_pool2
id: f2e9d8c7-6b5a-4f3e-8d2c-1b7a9e4d3f2e
description: The first BIND9 pool of the other multipools file
attributes:
type: internal
ns_records:
- hostname: ns1-1.example.org.
priority: 1
- hostname: ns1-2.example.org.
priority: 2
nameservers:
- host: 192.0.2.2
port: 1053
# - host: 192.0.2.3
# port: 53
targets:
- type: bind9
description: BIND9 Server 1
masters:
- host: 192.0.2.1
port: 5354
options:
host: 192.0.2.2
port: 1053
rndc_host: 192.0.2.2
rndc_port: 1953
rndc_key_file: /etc/designate/rndc.key
tsigkey_name: other-pools-pool-2
- name: default
description: Default BIND9 Pool
attributes: {}
ns_records:
- hostname: ns1-1.example.org.
priority: 1
- hostname: ns1-2.example.org.
priority: 2
nameservers:
- host: 192.0.2.2
port: 53
# - host: 192.0.2.3
# port: 53
targets:
- type: bind9
description: Default BIND9 Server
masters:
- host: 192.0.2.1
port: 5354
options:
host: 192.0.2.2
port: 53
rndc_host: 192.0.2.2
rndc_port: 953
rndc_key_file: /etc/designate/rndc.key

View File

@ -0,0 +1,246 @@
# Copyright 2024 Red Hat
#
# 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 os
import random
import testtools
import yaml
from oslo_log import log as logging
from tempest import config
import subprocess
from tempest.lib import decorators
from designate_tempest_plugin.tests import base
from designate_tempest_plugin.tests import resources
CONF = config.CONF
LOG = logging.getLogger(__name__)
class DesignateManageTest(base.BaseDnsV2Test):
credentials = ["admin", 'primary', 'system_admin']
managed_resource = None
@classmethod
def skip_checks(cls) -> None:
super().skip_checks()
if CONF.dns_feature_enabled.designate_manage_path:
cls.designate_manage_cmd = (
CONF.dns_feature_enabled.designate_manage_path)
else:
raise cls.skipException('designate-manage path was not found. '
'Skipping this test class')
@classmethod
def _run_designate_manage_command(cls,
managed_resource: str,
command: str,
*args) -> str:
"""Runs the designate-manage command with the provided arguments.
:param managed_resource: (str): The resource managed by the
designate-manage command. For example: pool
:param command: (str): The command to run. For example: update
:return: The command output
"""
managed_resource = managed_resource or cls.managed_resource
commands_list = [cls.designate_manage_cmd, managed_resource]
if command and isinstance(command, tuple):
commands_list.extend(command)
else:
commands_list.append(command)
if args and isinstance(args, tuple):
commands_list.extend(args[0])
try:
output = subprocess.check_output(commands_list,
stderr=subprocess.STDOUT,
text=True)
return output
except subprocess.CalledProcessError as e:
LOG.error(e.output)
def _get_pools_path(name: str) -> str:
return os.path.join(resources.path, 'pools_yaml', name)
@testtools.skipUnless(CONF.dns_feature_enabled.test_multipool_with_delete_opt,
'Multipools feature is being tested with --delete '
'option. It might delete pools that were created in '
'other tests.')
class DesignateManagePoolTest(DesignateManageTest):
managed_resource = 'pool'
file_attributes_to_num_of_appearances = { # per each Pool
'also_notifies:': 1, 'attributes:': 1, 'description:': 2, 'id:': 1,
'name:': 2, 'nameservers:': 1, 'ns_records:': 1, 'targets:': 1
}
MULTIPOOLS_FILE_PATH = "/etc/designate/multiple-pools.yaml"
@classmethod
def resource_setup(cls):
testtools.skipUnless(os.path.exists(cls.MULTIPOOLS_FILE_PATH),
f"multiple-pools configuration file {cls.MULTIPOOLS_FILE_PATH} was "
"not found, skipping the test")
cls._update_pools_file(cls.MULTIPOOLS_FILE_PATH)
@classmethod
def _update_pools_file(cls, pools_file_path):
if not pools_file_path.startswith('/'):
pools_file_path = _get_pools_path(name=pools_file_path)
cls._run_designate_manage_pool_command(
'update',
'--file',
pools_file_path,
'--delete')
def tearDown(self):
super(DesignateManagePoolTest, self).tearDown()
self._update_pools_file(self.MULTIPOOLS_FILE_PATH)
@staticmethod
def _load_config(filename) -> str:
with open(filename) as stream:
return yaml.safe_load(stream)
@classmethod
def _run_designate_manage_pool_command(cls, command: str, *args) -> str:
return super(
DesignateManagePoolTest, cls)._run_designate_manage_command(
'pool', command, args)
@decorators.idempotent_id('ed42f367-e5ba-40d7-a08d-366ad787d21d')
def test_pool_show_config(self):
self._update_pools_file(self.MULTIPOOLS_FILE_PATH)
pool_config = self._run_designate_manage_pool_command(
command='show_config').split('\n\n')[0]
self.assertIn('BIND Pool', pool_config)
@decorators.idempotent_id('ed42f367-e5ba-40d7-a08d-366ad787d21e')
def test_pool_show_config_all(self):
self._update_pools_file(pools_file_path='multiple-pools.yaml')
pool_config = self._run_designate_manage_pool_command(
'show_config', '--all').split('\n\n')[0]
pool_config_list = pool_config.split('\n')
for attribute in self.file_attributes_to_num_of_appearances:
num_of_occurrences = sum(attribute in s for s in pool_config_list)
file_attributes_to_num_of_appearances = {
'also_notifies:': 2, 'attributes:': 2, 'description:': 4,
'id:': 2, 'name:': 7, 'nameservers:': 2, 'ns_records:': 2,
'targets:': 2
}
err_msg = (f'{attribute} was supposed to appear '
f'{file_attributes_to_num_of_appearances[attribute]} '
'times on the designate-manage output, but '
f'it appeared {num_of_occurrences} times.')
self.assertEqual(
file_attributes_to_num_of_appearances[attribute],
num_of_occurrences, err_msg)
@decorators.idempotent_id('ed42f367-e5ba-40d7-a08d-366ad787d220')
def test_pool_update_multiple_pools_without_delete(self):
# Updating to multiple-pools.yaml with --delete
self._update_pools_file(pools_file_path='multiple-pools.yaml')
# Updating to other-pools.yaml without --delete. There should be 5
# pools after the update
pools_yaml_path = _get_pools_path(name='other-pools.yaml')
self._run_designate_manage_pool_command(
'update',
'--file',
pools_yaml_path,
)
pool_config = self._run_designate_manage_pool_command(
'show_config', '--all').split('\n\n')[0]
pool_config_list = pool_config.split('\n')
for attribute in self.file_attributes_to_num_of_appearances:
num_of_occurrences = sum(attribute in s for s in pool_config_list)
file_attributes_to_num_of_appearances = {
'also_notifies:': 3, 'attributes:': 3, 'description:': 6,
'id:': 3, 'name:': 11, 'nameservers:': 3, 'ns_records:': 3,
'targets:': 3
}
err_msg = (f'{attribute} was supposed to appear '
f'{file_attributes_to_num_of_appearances[attribute]} '
'times on the designate-manage output, but '
f'it appeared {num_of_occurrences} times.')
self.assertEqual(
file_attributes_to_num_of_appearances[attribute],
num_of_occurrences, err_msg)
@decorators.idempotent_id('ed42f367-e5ba-40d7-a08d-366ad787d223')
def test_pool_update_multiple_pools_dry_run(self):
self._update_pools_file(pools_file_path='multiple-pools.yaml')
pools_yaml_path = _get_pools_path(name='other-pools.yaml')
self._run_designate_manage_pool_command(
'update',
'--file',
pools_yaml_path,
'--dry-run'
)
pool_config = self._run_designate_manage_pool_command(
'show_config', '--all').split('\n\n')[0]
pool_config_list = pool_config.split('\n')
for attribute in self.file_attributes_to_num_of_appearances:
num_of_occurrences = sum(attribute in s for s in pool_config_list)
file_attributes_to_num_of_appearances = {
'also_notifies:': 2, 'attributes:': 2, 'description:': 4,
'id:': 2, 'name:': 7, 'nameservers:': 2, 'ns_records:': 2,
'targets:': 2
}
err_msg = (f'{attribute} was supposed to appear '
f'{file_attributes_to_num_of_appearances[attribute]} '
'times on the designate-manage output, but '
f'it appeared {num_of_occurrences} times.')
self.assertEqual(
file_attributes_to_num_of_appearances[attribute],
num_of_occurrences, err_msg)
@decorators.idempotent_id('ed42f367-e5ba-40d7-a08d-366ad787d224')
def test_pool_generate_file(self):
temp_pools_yaml_conf_path = '/tmp/pools_tempest.yaml'
if os.path.exists(temp_pools_yaml_conf_path):
LOG.debug(f'Temporary pools.yaml file {temp_pools_yaml_conf_path} '
'exists.\nRemoving this file so we could continue '
'testing')
os.remove(temp_pools_yaml_conf_path)
self._run_designate_manage_pool_command('generate_file',
'--file',
temp_pools_yaml_conf_path)
self.assertTrue(os.path.exists(path=temp_pools_yaml_conf_path))
# (At least) the default pool config should be written to the file
pools_conf = self._load_config(filename=temp_pools_yaml_conf_path)
if len(pools_conf) > 1:
pool_idx = random.randint(0, len(pools_conf) - 1)
else:
pool_idx = 0
pool = yaml.safe_dump(pools_conf[pool_idx]).split('\n')
for attribute in self.file_attributes_to_num_of_appearances:
self.assertTrue(
any(attribute in s for s in pool),
f'{attribute} not in {pool}'
)
if os.path.exists(temp_pools_yaml_conf_path):
os.remove(temp_pools_yaml_conf_path)

View File

@ -12,6 +12,10 @@ function _configure_tempest {
if [ -n "$DESIGNATE_BIN_DIR" ]; then
iniset $TEMPEST_CONFIG dns_feature_enabled designate_manage_path ${DESIGNATE_BIN_DIR}/designate-manage
fi
POOLS_YAML_PATH=/etc/designate/multiple-pools.yaml
cp /etc/designate/pools.yaml ${POOLS_YAML_PATH}
sed -i 's/"pool_level": "secondary"/"pool_level": "tertiary"/' ${POOLS_YAML_PATH}
}
if [[ "$1" == "stack" ]]; then