Merge "doc: Update the module of services decorator"
diff --git a/.zuul.yaml b/.zuul.yaml
index be0cef3..bea1b4c 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -1,4 +1,10 @@
 - job:
+    name: designate-bind9-stable-2024-1
+    parent: designate-bind9
+    nodeset: openstack-single-node-jammy
+    override-checkout: stable/2024.1
+
+- job:
     name: designate-bind9-stable-bobcat
     parent: designate-bind9
     nodeset: openstack-single-node-jammy
@@ -10,18 +16,6 @@
     nodeset: openstack-single-node-focal
     override-checkout: stable/2023.1
 
-- job:
-    name: designate-bind9-stable-zed
-    parent: designate-bind9
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/zed
-
-- job:
-    name: designate-bind9-stable-yoga
-    parent: designate-bind9
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/yoga
-
 - project:
     templates:
       - designate-devstack-jobs
@@ -31,8 +25,7 @@
       - release-notes-jobs-python3
     check:
       jobs:
+        - designate-bind9-stable-2024-1
         - designate-bind9-stable-bobcat
         - designate-bind9-stable-antelope
-        - designate-bind9-stable-zed
-        - designate-bind9-stable-yoga
         - neutron-tempest-plugin-designate-scenario
diff --git a/designate_tempest_plugin/config.py b/designate_tempest_plugin/config.py
index 2144eb1..9b86945 100644
--- a/designate_tempest_plugin/config.py
+++ b/designate_tempest_plugin/config.py
@@ -42,7 +42,7 @@
                help="Timeout in seconds to wait for an resource to build."),
     cfg.IntOpt('min_ttl',
                default=0,
-               help="The minimum value to respect when generating ttls"),
+               help="The minimum value to respect when generating ttl"),
     cfg.ListOpt('nameservers',
                 default=[],
                 help="The nameservers to check for change going live"),
diff --git a/designate_tempest_plugin/tests/api/v2/test_ptrs.py b/designate_tempest_plugin/tests/api/v2/test_ptrs.py
index a72b61a..dcb31a9 100644
--- a/designate_tempest_plugin/tests/api/v2/test_ptrs.py
+++ b/designate_tempest_plugin/tests/api/v2/test_ptrs.py
@@ -13,6 +13,7 @@
 # under the License.
 from oslo_log import log as logging
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -23,7 +24,7 @@
 from designate_tempest_plugin.common import waiters
 from designate_tempest_plugin import data_utils as dns_data_utils
 
-import tempest.test
+import tempest.api.network.base
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -41,6 +42,8 @@
             cls.admin_tld_client = cls.os_system_admin.dns_v2.TldClient()
         else:
             cls.admin_tld_client = cls.os_admin.dns_v2.TldClient()
+        cls.admin_network_client = cls.os_admin.networks_client
+        cls.admin_subnet_client = cls.os_admin.subnets_client
 
     @classmethod
     def resource_setup(cls):
@@ -51,6 +54,26 @@
         cls.tld_name = tld_name[:-1]
         cls.class_tld = cls.admin_tld_client.create_tld(tld_name=tld_name[:-1])
 
+        # Create dedicated External network for PTRs tests
+        network_name = data_utils.rand_name('designate-tempest-fips-external')
+        cls.external_network = cls.admin_network_client.create_network(
+            name=network_name, **{'router:external': True})['network']
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.admin_network_client.delete_network,
+            cls.external_network['id'])
+
+        # Create subnet for External network
+        cls.external_subnet = cls.admin_subnet_client.create_subnet(
+            network_id=cls.external_network['id'],
+            allocation_pools=[
+                {'start': '198.51.100.10', 'end': '198.51.100.200'}],
+            cidr='198.51.100.0/24', ip_version=4, enable_dhcp=False)
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.admin_subnet_client.delete_subnet,
+            cls.external_subnet['subnet']['id'])
+
     @classmethod
     def resource_cleanup(cls):
         cls.admin_tld_client.delete_tld(cls.class_tld[1]['id'])
@@ -97,7 +120,7 @@
             tld = self.tld_name
         if not fip_id:
             fip = self.primary_floating_ip_client.create_floatingip(
-                floating_network_id=CONF.network.public_network_id)[
+                floating_network_id=self.external_network['id'])[
                 'floatingip']
             fip_id = fip['id']
             self.addCleanup(
@@ -221,7 +244,7 @@
             tld = self.tld_name
         if not fip_id:
             fip = self.primary_floating_ip_client.create_floatingip(
-                floating_network_id=CONF.network.public_network_id)[
+                floating_network_id=self.external_network['id'])[
                 'floatingip']
             fip_id = fip['id']
             self.addCleanup(
diff --git a/designate_tempest_plugin/tests/api/v2/test_zones.py b/designate_tempest_plugin/tests/api/v2/test_zones.py
index d1c3a11..d971790 100644
--- a/designate_tempest_plugin/tests/api/v2/test_zones.py
+++ b/designate_tempest_plugin/tests/api/v2/test_zones.py
@@ -580,19 +580,19 @@
                  '"x-auth-sudo-project-id" HTTP header. '
                  'Expected: 403 Forbidden')
         self.assertRaises(
-            lib_exc.Forbidden, self.alt_client.show_zone, uuid=None,
+            lib_exc.Forbidden, self.alt_client.show_zone, uuid=zone['id'],
             headers={'x-auth-sudo-project-id': zone['project_id']})
 
         LOG.info('As Admin user impersonate another project '
                  '(using "x-auth-sudo-project-id" HTTP header) to show '
                  'a Primary tenant zone.')
         body = self.admin_client.show_zone(
-            uuid=None, headers={
+            uuid=zone['id'], headers={
                 'x-auth-sudo-project-id': zone['project_id']})[1]
 
         LOG.info('Ensure the fetched response matches the impersonated'
                  ' project, it means the ID of a zone "A"')
-        self.assertExpected(zone, body['zones'][0], self.excluded_keys)
+        self.assertExpected(zone, body, self.excluded_keys)
 
     @decorators.idempotent_id('e1cf7104-8b06-11eb-a861-74e5f9e2a801')
     def test_list_all_projects_zones(self):
@@ -732,10 +732,10 @@
 
     @decorators.idempotent_id('551853c0-8593-11eb-8c8a-74e5f9e2a801')
     def test_no_valid_ttl(self):
-        no_valid_tls = ['zahlabut', -60000,
+        no_valid_ttl = ['zahlabut', -60000,
                         2147483647 + 10]  # Max valid TTL is 2147483647
 
-        for ttl in no_valid_tls:
+        for ttl in no_valid_ttl:
             LOG.info(
                 'Trying to create a zone using: {} as TTL'
                 ' value: '.format(ttl))
@@ -743,6 +743,13 @@
                 lib_exc.BadRequest, 'invalid_object', 400,
                 self.zones_client.create_zone, ttl=ttl)
 
+    @decorators.idempotent_id('c4d4b92a-86e3-11ee-965f-201e8823901f')
+    def test_not_existing_project_id(self):
+        LOG.info('Trying to create a zone using not existing project_id')
+        self.assertRaises(
+            lib_exc.Forbidden, self.zones_client.create_zone,
+            project_id=data_utils.rand_uuid())
+
     @decorators.idempotent_id('a3b0a928-a682-11eb-9899-74e5f9e2a801')
     def test_huge_size_description(self):
         LOG.info('Trying to create a zone using huge size description')
diff --git a/setup.cfg b/setup.cfg
index 728e365..72c6235 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -21,6 +21,7 @@
     Programming Language :: Python :: 3.6
     Programming Language :: Python :: 3.7
     Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
     Topic :: Internet :: Name Service (DNS)
 
 [files]