Merge "Adds an API test for list transfer requests sudo"
diff --git a/designate_tempest_plugin/common/constants.py b/designate_tempest_plugin/common/constants.py
index 84ee5ae..4f6c72b 100644
--- a/designate_tempest_plugin/common/constants.py
+++ b/designate_tempest_plugin/common/constants.py
@@ -19,6 +19,7 @@
 DELETED = 'DELETED'
 ACTIVE = 'ACTIVE'
 UP = 'UP'
+CREATE = 'CREATE'
 
 # Zone types
 PRIMARY_ZONE_TYPE = 'PRIMARY'
diff --git a/designate_tempest_plugin/common/exceptions.py b/designate_tempest_plugin/common/exceptions.py
new file mode 100644
index 0000000..d3f343d
--- /dev/null
+++ b/designate_tempest_plugin/common/exceptions.py
@@ -0,0 +1,29 @@
+# Copyright 2021 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.
+
+
+class InvalidStatusError(Exception):
+    """
+    Exception raise when an entity changes to an unexpected status.
+    """
+
+    def __init__(self, entity, entity_id, status, expected_status=None):
+        if expected_status:
+            message = ("{0} with ID {1} returned status {2} when {3} was "
+                       "expected.".format(entity, entity_id,
+                                          status, expected_status))
+        else:
+            message = ("{0} with ID {1} returned unexpected status {2}".format(
+                entity, entity_id, status))
+        super(InvalidStatusError, self).__init__(message)
diff --git a/designate_tempest_plugin/common/waiters.py b/designate_tempest_plugin/common/waiters.py
index 2b7a3b6..6bc5dfa 100644
--- a/designate_tempest_plugin/common/waiters.py
+++ b/designate_tempest_plugin/common/waiters.py
@@ -18,6 +18,9 @@
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 
+from designate_tempest_plugin.common import constants as const
+from designate_tempest_plugin.common import exceptions
+
 LOG = logging.getLogger(__name__)
 
 
@@ -35,6 +38,10 @@
             LOG.info('Zone %s is 404ing', zone_id)
             return
 
+        if zone['status'] == const.ERROR:
+            raise exceptions.InvalidStatusError('Zone', zone_id,
+                                                zone['status'])
+
         if int(time.time()) - start >= client.build_timeout:
             message = ('Zone %(zone_id)s failed to 404 within the required '
                        'time (%(timeout)s s). Current status: '
@@ -226,3 +233,35 @@
                 message = "(%s) %s" % (caller, message)
 
             raise lib_exc.TimeoutException(message)
+
+
+def wait_for_ptr_status(client, fip_id, status):
+    """Waits for a PTR associated with FIP to reach given status."""
+    LOG.info('Waiting for PTR %s to reach %s', fip_id, status)
+
+    ptr = client.show_ptr_record(fip_id)
+    start = int(time.time())
+
+    while ptr['status'] != status:
+        time.sleep(client.build_interval)
+        ptr = client.show_ptr_record(fip_id)
+        status_curr = ptr['status']
+        if status_curr == status:
+            LOG.info('PTR %s reached %s', fip_id, status)
+            return
+
+        if int(time.time()) - start >= client.build_timeout:
+            message = ('PTR for FIP: %(fip_id)s failed to reach '
+                       'status=%(status)s within the required time '
+                       '(%(timeout)s s). Current status: %(status_curr)s' %
+                       {'fip_id': fip_id,
+                        'status': status,
+                        'status_curr': status_curr,
+                        'timeout': client.build_timeout})
+
+            caller = test_utils.find_test_caller()
+
+            if caller:
+                message = '(%s) %s' % (caller, message)
+
+            raise lib_exc.TimeoutException(message)
diff --git a/designate_tempest_plugin/data_utils.py b/designate_tempest_plugin/data_utils.py
index c9d3f6d..4b5d24d 100644
--- a/designate_tempest_plugin/data_utils.py
+++ b/designate_tempest_plugin/data_utils.py
@@ -12,6 +12,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 import random
+from string import ascii_lowercase
 
 import netaddr
 from oslo_log import log as logging
@@ -34,7 +35,7 @@
     return an.format(netaddr.ipv6_compact)
 
 
-def rand_zone_name(name='', prefix=None, suffix='.com.'):
+def rand_zone_name(name='', prefix='rand', suffix='.com.'):
     """Generate a random zone name
     :param str name: The name that you want to include
     :param prefix: the exact text to start the string. Defaults to "rand"
@@ -96,8 +97,8 @@
         quotas_dict['api_export_size'] = \
             api_export_size or data_utils.rand_int_id(100, 999999)
     else:
-        LOG.warn("Leaving `api_export_size` out of quota data due to: "
-                 "https://bugs.launchpad.net/designate/+bug/1573141")
+        LOG.warning("Leaving `api_export_size` out of quota data due to: "
+                    "https://bugs.launchpad.net/designate/+bug/1573141")
 
     return quotas_dict
 
@@ -261,3 +262,22 @@
 
     func = globals()["rand_{}_recordset".format(record_type.lower())]
     return func(zone_name)
+
+
+def rand_string(size):
+    """Create random string of ASCII chars by size
+
+    :param int size - length os the string to be create
+    :return - random creates string of ASCII lover characters
+    """
+    return ''.join(random.choice(ascii_lowercase) for _ in range(size))
+
+
+def rand_domain_name(tld=None):
+    """Create random valid domain name
+
+    :param tld (optional) - TLD that will be used to random domain name
+    :return - valid domain name, for example: paka.zbabun.iuh
+    """
+    domain_tld = tld or rand_string(3)
+    return rand_string(4) + '.' + rand_string(6) + '.' + domain_tld + '.'
diff --git a/designate_tempest_plugin/services/dns/json/base.py b/designate_tempest_plugin/services/dns/json/base.py
index cbfb34f..d484ac9 100644
--- a/designate_tempest_plugin/services/dns/json/base.py
+++ b/designate_tempest_plugin/services/dns/json/base.py
@@ -82,7 +82,7 @@
                        "received not-int read_code %(read_code)r" %
                        {'expected_code': expected_code,
                         'read_code': read_code})
-            LOG.warn(message)
+            LOG.warning(message)
         return super(DnsClientBase, cls).expected_success(
             expected_code=expected_code, read_code=int(read_code),
         )
diff --git a/designate_tempest_plugin/services/dns/v2/json/ptr_client.py b/designate_tempest_plugin/services/dns/v2/json/ptr_client.py
index 1bd59b3..eed221b 100644
--- a/designate_tempest_plugin/services/dns/v2/json/ptr_client.py
+++ b/designate_tempest_plugin/services/dns/v2/json/ptr_client.py
@@ -24,7 +24,8 @@
 
     @base.handle_errors
     def set_ptr_record(self, floatingip_id, ptr_name=None,
-                       ttl=None, description=None, headers=None):
+                       ttl=None, description=None, headers=None,
+                       tld=None):
         """Set a PTR record for the given FloatingIP
 
         :param floatingip_id: valid UUID of floating IP to be used.
@@ -32,13 +33,13 @@
         :param ttl TTL or random valid value if not provided.
         :param description Description or random if not provided.
         :param headers (dict): The headers to use for the request.
+        :param tld, the TLD to be used in ptrdname generated value.
         :return: created PTR dictionary.
         """
         ptr = {
-            'ptrdname': ptr_name or dns_data_utils.rand_zone_name(),
+            'ptrdname': ptr_name or dns_data_utils.rand_domain_name(tld),
             'ttl': ttl or dns_data_utils.rand_ttl(),
-            'description': description or data_utils.rand_name(
-                'test-ptr')}
+            'description': description or data_utils.rand_name('test-ptr')}
 
         return self._update_request(
             resource='reverse/floatingips/{}'.format(CONF.identity.region),
diff --git a/designate_tempest_plugin/tests/api/v2/test_enabled_api_version.py b/designate_tempest_plugin/tests/api/v2/test_enabled_api_version.py
index db98a9d..c5fcd69 100644
--- a/designate_tempest_plugin/tests/api/v2/test_enabled_api_version.py
+++ b/designate_tempest_plugin/tests/api/v2/test_enabled_api_version.py
@@ -51,8 +51,8 @@
                 versions = self.primary_client.list_enabled_api_versions()[1][
                     'versions']['values']
             if user == 'not_auth_user':
-                uri = CONF.identity.uri.split('identity')[0] + 'dns'
-                response = requests.get(uri, verify=False)
+                response = requests.get(self.primary_client.base_url,
+                                        verify=False)
                 headers = {
                     k.lower(): v.lower() for k, v in response.headers.items()}
                 versions = self.deserialize(
diff --git a/designate_tempest_plugin/tests/api/v2/test_ptrs.py b/designate_tempest_plugin/tests/api/v2/test_ptrs.py
index e2ad98a..b094a5d 100644
--- a/designate_tempest_plugin/tests/api/v2/test_ptrs.py
+++ b/designate_tempest_plugin/tests/api/v2/test_ptrs.py
@@ -13,15 +13,22 @@
 # under the License.
 from oslo_log import log as logging
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
 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 import data_utils as dns_data_utils
+
 import tempest.test
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
+TLD = dns_data_utils.rand_string(3)
+
 
 class BasePtrTest(base.BaseDnsV2Test):
     excluded_keys = ['created_at', 'updated_at', 'version', 'links',
@@ -29,7 +36,8 @@
 
 
 class DesignatePtrRecord(BasePtrTest, tempest.test.BaseTestCase):
-    credentials = ["primary"]
+
+    credentials = ['primary', 'admin', 'system_admin']
 
     @classmethod
     def setup_credentials(cls):
@@ -40,21 +48,37 @@
     @classmethod
     def setup_clients(cls):
         super(DesignatePtrRecord, cls).setup_clients()
+        if CONF.enforce_scope.designate:
+            cls.admin_ptr_client = cls.os_system_admin.dns_v2.PtrClient()
+        else:
+            cls.admin_ptr_client = cls.os_admin.dns_v2.PtrClient()
         cls.primary_ptr_client = cls.os_primary.dns_v2.PtrClient()
         cls.primary_floating_ip_client = cls.os_primary.floating_ips_client
 
-    def _set_ptr(self):
-        fip = self.primary_floating_ip_client.create_floatingip(
-            floating_network_id=CONF.network.public_network_id)['floatingip']
-        fip_id = fip['id']
-        self.addCleanup(self.primary_floating_ip_client.delete_floatingip,
-                        fip_id)
-        ptr = self.primary_ptr_client.set_ptr_record(fip_id)
+    def _set_ptr(self, ptr_name=None, ttl=None, description=None,
+                 headers=None, tld=TLD, fip_id=None):
+        if not fip_id:
+            fip = self.primary_floating_ip_client.create_floatingip(
+                floating_network_id=CONF.network.public_network_id)[
+                'floatingip']
+            fip_id = fip['id']
+            self.addCleanup(
+                self.primary_floating_ip_client.delete_floatingip, fip_id)
+        ptr = self.primary_ptr_client.set_ptr_record(
+            fip_id, ptr_name=ptr_name, ttl=ttl, description=description,
+            headers=headers, tld=tld)
         self.addCleanup(self.primary_ptr_client.unset_ptr_record, fip_id)
         self.assertEqual('CREATE', ptr['action'])
         self.assertEqual('PENDING', ptr['status'])
+        waiters.wait_for_ptr_status(
+            self.primary_ptr_client, fip_id=fip_id, status=const.ACTIVE)
         return fip_id, ptr
 
+    def _unset_ptr(self, fip_id):
+        self.primary_ptr_client.unset_ptr_record(fip_id)
+        waiters.wait_for_ptr_status(
+            self.primary_ptr_client, fip_id=fip_id, status=const.DELETED)
+
     @decorators.idempotent_id('2fb9d6ea-871d-11eb-9f9a-74e5f9e2a801')
     def test_set_floatingip_ptr(self):
         self._set_ptr()
@@ -66,6 +90,19 @@
             floatingip_id=fip_id)
         self.assertExpected(ptr, show_ptr, self.excluded_keys)
 
+    @decorators.idempotent_id('d3128a92-e3bd-11eb-a097-74e5f9e2a801')
+    def test_show_floatingip_ptr_impersonate_another_project(self):
+        fip_id, ptr = self._set_ptr()
+
+        LOG.info('As Admin user, show PTR record created by Primary'
+                 ' user by including "x-auth-sudo-project-id" HTTP header'
+                 ' in HTTP request.')
+        show_ptr = self.admin_ptr_client.show_ptr_record(
+            floatingip_id=fip_id,
+            headers={
+                'x-auth-sudo-project-id': self.primary_ptr_client.project_id})
+        self.assertExpected(ptr, show_ptr, self.excluded_keys)
+
     @decorators.idempotent_id('9187a9c6-87d4-11eb-9f9a-74e5f9e2a801')
     def test_list_floatingip_ptr_records(self):
         number_of_ptr_records = 3
@@ -81,14 +118,34 @@
             'Failed - received PTR IDs: {} are not as'
             ' expected: {}'.format(created_ptr_ids, received_ptr_ids))
 
+    @decorators.idempotent_id('a108d6f2-e3c0-11eb-a097-74e5f9e2a801')
+    @decorators.skip_because(bug="1935977")
+    def test_list_floatingip_ptr_all_projects(self):
+        ptr = self._set_ptr()[1]
+        LOG.info('Created PTR is:{}'.format(ptr))
+
+        LOG.info('As Admin user, try to list PTR record for all projects '
+                 'by including "x-auth-all-projects" HTTP header.')
+        received_ptr_ids = [
+            item['id'] for item in self.admin_ptr_client.list_ptr_records(
+                headers={'x-auth-all-projects': True})]
+        self.assertGreater(
+            len(received_ptr_ids), 0,
+            'Failed, "received_ptr_ids" should not be empty')
+        self.assertIn(
+            ptr['id'], received_ptr_ids,
+            'Failed, expected ID was not found in "received_ptr_ids" list.')
+
     @decorators.idempotent_id('499b5a7e-87e1-11eb-b412-74e5f9e2a801')
+    @decorators.skip_because(bug="1932026")
     def test_unset_floatingip_ptr(self):
         fip_id, ptr = self._set_ptr()
-        self.primary_ptr_client.unset_ptr_record(fip_id)
+        self._unset_ptr(fip_id)
 
 
 class DesignatePtrRecordNegative(BasePtrTest, tempest.test.BaseTestCase):
-    credentials = ["primary"]
+
+    credentials = ['primary', 'admin']
 
     @classmethod
     def setup_credentials(cls):
@@ -101,24 +158,79 @@
         super(DesignatePtrRecordNegative, cls).setup_clients()
         cls.primary_ptr_client = cls.os_primary.dns_v2.PtrClient()
         cls.primary_floating_ip_client = cls.os_primary.floating_ips_client
+        cls.admin_ptr_client = cls.os_admin.dns_v2.PtrClient()
 
     def _set_ptr(self, ptr_name=None, ttl=None, description=None,
-                 headers=None):
-        fip = self.primary_floating_ip_client.create_floatingip(
-            floating_network_id=CONF.network.public_network_id)[
-            'floatingip']
-        fip_id = fip['id']
-        self.addCleanup(self.primary_floating_ip_client.delete_floatingip,
-                        fip_id)
+                 headers=None, tld=TLD, fip_id=None):
+        if not fip_id:
+            fip = self.primary_floating_ip_client.create_floatingip(
+                floating_network_id=CONF.network.public_network_id)[
+                'floatingip']
+            fip_id = fip['id']
+            self.addCleanup(
+                self.primary_floating_ip_client.delete_floatingip, fip_id)
         ptr = self.primary_ptr_client.set_ptr_record(
             fip_id, ptr_name=ptr_name, ttl=ttl, description=description,
-            headers=headers)
+            headers=headers, tld=tld)
         self.addCleanup(self.primary_ptr_client.unset_ptr_record, fip_id)
         self.assertEqual('CREATE', ptr['action'])
         self.assertEqual('PENDING', ptr['status'])
+        waiters.wait_for_ptr_status(
+            self.primary_ptr_client, fip_id=fip_id, status=const.ACTIVE)
         return fip_id, ptr
 
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('8392db50-cdd0-11eb-a00f-74e5f9e2a801')
     def test_set_floatingip_ptr_invalid_ttl(self):
         LOG.info('Try to set PTR record using invalid TTL value')
         with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_object', 400):
             self._set_ptr(ttl=-10)
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('0c9349ae-e2e8-11eb-a097-74e5f9e2a801')
+    def test_set_floatingip_ptr_not_existing_fip_id(self):
+        LOG.info('Try to set PTR record using not existing Floating IP')
+        with self.assertRaisesDns(lib_exc.NotFound, 'not_found', 404):
+            self._set_ptr(fip_id=data_utils.rand_uuid())
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('df217902-e3b2-11eb-a097-74e5f9e2a801')
+    def test_set_floatingip_ptr_huge_size_description(self):
+        LOG.info('Try to set PTR record using huge size description string')
+        with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_object', 400):
+            self._set_ptr(description=dns_data_utils.rand_string(5000))
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('cb2264e2-e3b3-11eb-a097-74e5f9e2a801')
+    def test_set_floatingip_ptr_invalid_name(self):
+        invalid_names = ['', '@!(*&', 4564, dns_data_utils.rand_string(5000)]
+        for name in invalid_names:
+            LOG.info('Set PTR record using invalid name:{}'.format(name))
+            with self.assertRaisesDns(
+                    lib_exc.BadRequest, 'invalid_object', 400):
+                self._set_ptr(description=dns_data_utils.rand_string(5000))
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('f616d216-51ac-11ec-8edf-201e8823901f')
+    def test_show_floatingip_ptr_impersonate_another_project_no_header(self):
+        fip_id, ptr = self._set_ptr()
+
+        LOG.info('As Admin user, show PTR record created by Primary'
+                 ' user, without including "x-auth-sudo-project-id" '
+                 'HTTP header in request.')
+        with self.assertRaisesDns(lib_exc.NotFound, 'not_found', 404):
+            self.admin_ptr_client.show_ptr_record(floatingip_id=fip_id)
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('0d132ff0-51ad-11ec-8edf-201e8823901f')
+    @decorators.skip_because(bug="1935977")
+    def test_list_floatingip_ptr_all_projects_no_header(self):
+        ptr = self._set_ptr()[1]
+        LOG.info('Created PTR is:{}'.format(ptr))
+
+        LOG.info('As Admin user, try to list PTR record for all projects '
+                 'without including "x-auth-all-projects" HTTP header.')
+        received_ptr_ids = [
+            item['id'] for item in self.admin_ptr_client.list_ptr_records()]
+        self.assertEqual([], received_ptr_ids,
+                         'Failed, "received_ptr_ids" list should be empty')
diff --git a/designate_tempest_plugin/tests/scenario/v2/recordset_data.json b/designate_tempest_plugin/tests/scenario/v2/recordset_data.json
index 9f365a7..3168722 100644
--- a/designate_tempest_plugin/tests/scenario/v2/recordset_data.json
+++ b/designate_tempest_plugin/tests/scenario/v2/recordset_data.json
@@ -48,5 +48,37 @@
         "name": "www",
         "type": "TXT",
         "records": ["\"Any Old Text Goes Here\""]
+    },
+    "SPF": {
+        "name": "*.sub",
+        "type": "SPF",
+        "records": ["\"v=spf1; a -all\""]
+    },
+    "NS": {
+        "name": "NS_Record",
+        "type": "NS",
+        "records": ["ns1.example.org."]
+    },
+    "PTR_IPV4": {
+        "name": "PTR_Record_IPV4",
+        "type": "PTR",
+        "records": ["34.216.184.93.in-addr.arpa."]
+    },
+   "PTR_IPV6":{
+       "name":"PTR_Record_IPV6",
+       "type":"PTR",
+       "records":[
+           "6.4.9.1.8.c.5.2.3.9.8.1.8.4.2.0.1.0.0.0.0.2.2.0.0.0.8.2.6.0.6.2.ip6.arpa."
+      ]
+   },
+   "CAA_Record": {
+       "name": "CAA_Record",
+       "type": "CAA",
+       "records": ["0 issue letsencrypt.org"]
+    },
+   "NAPTR_Record": {
+       "name": "NAPTR_Record",
+       "type": "NAPTR",
+       "records": ["0 0 S SIP+D2U !^.*$!sip:customer-service@example.com! _sip._udp.example.com."]
     }
-}
+}
\ No newline at end of file
diff --git a/designate_tempest_plugin/tests/scenario/v2/test_blacklists.py b/designate_tempest_plugin/tests/scenario/v2/test_blacklists.py
index 1501300..27b3f7f 100644
--- a/designate_tempest_plugin/tests/scenario/v2/test_blacklists.py
+++ b/designate_tempest_plugin/tests/scenario/v2/test_blacklists.py
@@ -13,12 +13,14 @@
 # under the License.
 
 from oslo_log import log as logging
+from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
 from designate_tempest_plugin import data_utils as dns_data_utils
 from designate_tempest_plugin.tests import base
 
+CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
@@ -28,7 +30,7 @@
 
 class BlacklistE2E(BaseBlacklistsTest):
 
-    credentials = ["admin", 'primary']
+    credentials = ["admin", 'primary', 'system_admin']
 
     @classmethod
     def setup_credentials(cls):
@@ -39,8 +41,13 @@
     @classmethod
     def setup_clients(cls):
         super(BlacklistE2E, cls).setup_clients()
-        cls.admin_blacklist_client = cls.os_admin.dns_v2.BlacklistsClient()
-        cls.admin_zone_client = cls.os_admin.dns_v2.ZonesClient()
+        if CONF.enforce_scope.designate:
+            cls.admin_blacklist_client = (
+                cls.os_system_admin.dns_v2.BlacklistsClient())
+            cls.admin_zone_client = cls.os_system_admin.dns_v2.ZonesClient()
+        else:
+            cls.admin_blacklist_client = cls.os_admin.dns_v2.BlacklistsClient()
+            cls.admin_zone_client = cls.os_admin.dns_v2.ZonesClient()
         cls.primary_zone_client = cls.os_primary.dns_v2.ZonesClient()
 
     @decorators.idempotent_id('22b1ee72-d8d2-11eb-bcdc-74e5f9e2a801')
@@ -94,9 +101,11 @@
                  'supposed to be blocked')
         zone = self.admin_zone_client.create_zone(
             name='blacklistnameregextest2' +
-                 dns_data_utils.rand_zone_name())[1]
+                 dns_data_utils.rand_zone_name(),
+            project_id=self.primary_zone_client.project_id)[1]
         self.addCleanup(
             self.wait_zone_delete, self.admin_zone_client, zone['id'])
-        zone = self.admin_zone_client.create_zone(name=zone_name)[1]
+        zone = self.admin_zone_client.create_zone(
+            name=zone_name, project_id=self.primary_zone_client.project_id)[1]
         self.addCleanup(
             self.wait_zone_delete, self.admin_zone_client, zone['id'])
diff --git a/designate_tempest_plugin/tests/scenario/v2/test_recordsets.py b/designate_tempest_plugin/tests/scenario/v2/test_recordsets.py
index 0808f56..4c40c28 100644
--- a/designate_tempest_plugin/tests/scenario/v2/test_recordsets.py
+++ b/designate_tempest_plugin/tests/scenario/v2/test_recordsets.py
@@ -27,10 +27,16 @@
 
 @ddt.ddt
 class RecordsetsTest(base.BaseDnsV2Test):
+
+    credentials = ["admin", "system_admin", "primary"]
+
     @classmethod
     def setup_clients(cls):
         super(RecordsetsTest, cls).setup_clients()
-
+        if CONF.enforce_scope.designate:
+            cls.admin_client = cls.os_system_admin.dns_v2.RecordsetClient()
+        else:
+            cls.admin_client = cls.os_admin.dns_v2.RecordsetClient()
         cls.client = cls.os_primary.dns_v2.ZonesClient()
         cls.recordset_client = cls.os_primary.dns_v2.RecordsetClient()
 
@@ -98,3 +104,21 @@
         self.assertRaises(lib_exc.NotFound,
                           lambda: self.recordset_client.show_recordset(
                               self.zone['id'], recordset['id']))
+
+    @decorators.idempotent_id('1e78a742-66ee-11ec-8dc3-201e8823901f')
+    def test_create_soa_record_not_permitted(self):
+        # SOA record is automatically created for a zone, no user
+        # should be able to create a SOA record.
+        soa_record = ("s1.devstack.org. admin.example.net. 1510721487 3510"
+                      " 600 86400 3600")
+        LOG.info('Primary tries to create a Recordset on '
+                 'the existing zone')
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self.recordset_client.create_recordset,
+            self.zone['id'], soa_record)
+        LOG.info('Admin tries to create a Recordset on the existing zone')
+        self.assertRaises(
+            lib_exc.BadRequest,
+            self.admin_client.create_recordset,
+            self.zone['id'], soa_record)
diff --git a/setup.cfg b/setup.cfg
index 3cf22d8..728e365 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,12 +1,12 @@
 [metadata]
 name = designate-tempest-plugin
 summary = OpenStack DNS As A Service (Designate) Functional Tests
-description-file =
+description_file =
     README.rst
 author = OpenStack
-author-email = openstack-discuss@lists.openstack.org
-home-page = https://docs.openstack.org/designate-tempest-plugin/latest/
-python-requires = >=3.6
+author_email = openstack-discuss@lists.openstack.org
+home_page = https://docs.openstack.org/designate-tempest-plugin/latest/
+python_requires = >=3.6
 classifier =
     Environment :: OpenStack
     Environment :: No Input/Output (Daemon)