Wait for reverse zone ACTIVE before creating PTRs

Related-Prod: PRODX-41812
Change-Id: I5639a50834cf7c8d74b6672500900f9b5a5d84f6
diff --git a/designate_tempest_plugin/tests/api/v2/test_ptrs.py b/designate_tempest_plugin/tests/api/v2/test_ptrs.py
index cf97d16..fdc2b74 100644
--- a/designate_tempest_plugin/tests/api/v2/test_ptrs.py
+++ b/designate_tempest_plugin/tests/api/v2/test_ptrs.py
@@ -39,10 +39,33 @@
         super(BasePtrTest, cls).setup_clients()
 
         cls.admin_tld_client = cls.os_admin.dns_v2.TldClient()
+        cls.admin_zones_client = cls.os_admin.dns_v2.ZonesClient()
         cls.admin_network_client = cls.os_admin.networks_client
         cls.admin_subnet_client = cls.os_admin.subnets_client
 
     @classmethod
+    def _ipv4_reverse_zone_name(cls, ip_address):
+        octets = ip_address.split('.')
+        if len(octets) != 4:
+            raise ValueError('Expected IPv4 address, got %r' % (ip_address,))
+        return '{}.{}.{}.in-addr.arpa.'.format(
+            octets[2], octets[1], octets[0])
+
+    def _wait_reverse_zone_active_if_exists(self):
+        zone_name = self.ptr_test_reverse_zone_name
+        headers = self.all_projects_header
+        zones = self.admin_zones_client.list_zones(
+            params={'name': zone_name}, headers=headers)[1]['zones']
+        if not zones:
+            return
+        waiters.wait_for_zone_status(
+            self.admin_zones_client,
+            zones[0]['id'],
+            const.ACTIVE,
+            headers=headers
+        )
+
+    @classmethod
     def resource_setup(cls):
         super(BasePtrTest, cls).resource_setup()
 
@@ -66,6 +89,10 @@
             cls.admin_subnet_client.delete_subnet,
             cls.external_subnet['subnet']['id'])
 
+        cls.ptr_test_reverse_zone_name = cls._ipv4_reverse_zone_name(
+            cls.external_subnet['subnet']['cidr'].split('/')[0]
+        )
+
 
 class DesignatePtrRecord(BasePtrTest, tempest.test.BaseTestCase):
 
@@ -100,6 +127,11 @@
             fip_id = fip['id']
             self.addCleanup(
                 self.primary_floating_ip_client.delete_floatingip, fip_id)
+        # PRODX-59322: PTR creates share one reverse zone; if the next
+        # update runs while the worker still sees an old SOA serial on NSs,
+        # the zone action fails and central ends the PTR in ERROR. Wait
+        # reverse zone ACTIVE first.
+        self._wait_reverse_zone_active_if_exists()
         ptr = self.primary_ptr_client.set_ptr_record(
             fip_id, ptr_name=ptr_name, ttl=ttl, description=description,
             headers=headers, tld=tld)
@@ -209,6 +241,11 @@
             fip_id = fip['id']
             self.addCleanup(
                 self.primary_floating_ip_client.delete_floatingip, fip_id)
+        # PRODX-59322: PTR creates share one reverse zone; if the next
+        # update runs while the worker still sees an old SOA serial on NSs,
+        # the zone action fails and central ends the PTR in ERROR. Wait
+        # reverse zone ACTIVE first.
+        self._wait_reverse_zone_active_if_exists()
         ptr = self.primary_ptr_client.set_ptr_record(
             fip_id, ptr_name=ptr_name, ttl=ttl, description=description,
             headers=headers, tld=tld)