Merge "Support py3.12 and drop py3.8"
diff --git a/.zuul.yaml b/.zuul.yaml
index 2a5cd41..ecda02d 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -16,12 +16,6 @@
     nodeset: openstack-single-node-jammy
     override-checkout: stable/2023.2
 
-- job:
-    name: designate-bind9-stable-2023-1
-    parent: designate-bind9
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/2023.1
-
 - project:
     templates:
       - designate-devstack-jobs
@@ -34,5 +28,4 @@
         - designate-bind9-stable-2024-2
         - designate-bind9-stable-2024-1
         - designate-bind9-stable-2023-2
-        - designate-bind9-stable-2023-1
         - neutron-tempest-plugin-designate-scenario
diff --git a/designate_tempest_plugin/config.py b/designate_tempest_plugin/config.py
index 9b86945..4f880e2 100644
--- a/designate_tempest_plugin/config.py
+++ b/designate_tempest_plugin/config.py
@@ -87,6 +87,9 @@
                 default=False,
                 help="Is https://bugs.launchpad.net/designate/+bug/1932026 "
                      "fixed"),
+    cfg.StrOpt('designate_manage_path',
+               default=None,
+               help="The designate-manage command path"),
     # Note: Also see the enforce_scope section (from tempest) for Designate API
     #       scope checking setting.
     cfg.BoolOpt('enforce_new_defaults',
diff --git a/designate_tempest_plugin/hacking/checks.py b/designate_tempest_plugin/hacking/checks.py
index 53de95c..bce63ef 100644
--- a/designate_tempest_plugin/hacking/checks.py
+++ b/designate_tempest_plugin/hacking/checks.py
@@ -15,7 +15,6 @@
 import re
 
 from hacking import core
-import pycodestyle
 
 # D701: Default parameter value is a mutable type
 # D702: Log messages require translation
@@ -49,8 +48,8 @@
 
 
 @core.flake8ext
-def mutable_default_arguments(physical_line, logical_line, filename):
-    if pycodestyle.noqa(physical_line):
+def mutable_default_arguments(logical_line, filename, noqa):
+    if noqa:
         return
 
     if mutable_default_argument_check.match(logical_line):
@@ -69,7 +68,7 @@
     N319
     """
     if logical_line.startswith("LOG.debug(_("):
-        yield(0, "D706: Don't translate debug level logs")
+        yield (0, "D706: Don't translate debug level logs")
 
 
 @core.flake8ext
@@ -90,7 +89,7 @@
         UNDERSCORE_IMPORT_FILES.append(filename)
     elif (translated_log.match(logical_line) or
          string_translation.match(logical_line)):
-        yield(0, "D703: Found use of _() without explicit import of _!")
+        yield (0, "D703: Found use of _() without explicit import of _!")
 
 
 @core.flake8ext
@@ -109,8 +108,8 @@
 
     matches = graduated_oslo_libraries_import_re.match(logical_line)
     if matches:
-        yield(0, "D704: Found import of %s. This oslo library has been "
-                 "graduated!" % matches.group(1))
+        yield (0, "D704: Found import of %s. This oslo library has been "
+               "graduated!" % matches.group(1))
 
 
 @core.flake8ext
@@ -133,14 +132,13 @@
     if re.search(r"\bbasestring\b", logical_line):
         msg = ("D707: basestring is not Python3-compatible, use "
                "str instead.")
-        yield(0, msg)
+        yield (0, msg)
 
 
 @core.flake8ext
 def check_python3_xrange(logical_line):
     if re.search(r"\bxrange\s*\(", logical_line):
-        yield(0, "D708: Do not use xrange. Use range for "
-                 "large loops.")
+        yield (0, "D708: Do not use xrange. Use range for large loops.")
 
 
 @core.flake8ext
@@ -152,7 +150,7 @@
     for OpenStack we can enforce not using it.
     """
     if "LOG.audit(" in logical_line:
-        yield(0, "D709: LOG.audit is deprecated, please use LOG.info!")
+        yield (0, "D709: LOG.audit is deprecated, please use LOG.info!")
 
 
 @core.flake8ext
@@ -162,7 +160,7 @@
     D710
     """
     if logical_line.startswith('LOG.warn('):
-        yield(0, "D710:Use LOG.warning() rather than LOG.warn()")
+        yield (0, "D710:Use LOG.warning() rather than LOG.warn()")
 
 
 @core.flake8ext
diff --git a/designate_tempest_plugin/tests/api/v2/test_designate_limits.py b/designate_tempest_plugin/tests/api/v2/test_designate_limits.py
index 638d035..86ca419 100644
--- a/designate_tempest_plugin/tests/api/v2/test_designate_limits.py
+++ b/designate_tempest_plugin/tests/api/v2/test_designate_limits.py
@@ -89,9 +89,8 @@
                  'are {}: '.format(existing_project_ids))
         all_project_limits = self.admin_client.list_designate_limits(
             headers={'x-auth-all-projects': True})
-        LOG.info(
-            'Retrieved designate limits by Admin user for all projects '
-            'are: '.format(all_project_limits))
+        LOG.info('Retrieved designate limits by Admin user for all projects '
+                 'are: %s', all_project_limits)
         received_project_ids = [
             item['project_id'] for item in all_project_limits]
         for project_id in existing_project_ids:
diff --git a/designate_tempest_plugin/tests/api/v2/test_zones_exports.py b/designate_tempest_plugin/tests/api/v2/test_zones_exports.py
index 1ba783d..67a0e6a 100644
--- a/designate_tempest_plugin/tests/api/v2/test_zones_exports.py
+++ b/designate_tempest_plugin/tests/api/v2/test_zones_exports.py
@@ -420,7 +420,7 @@
             'show_zonefile_bad_accept')
         # Tempest-lib _error_checker will raise UnexpectedResponseCode
         e = self.assertRaises(
-            lib_exc.UnexpectedResponseCode, self.client.show_exported_zonefile,
+            lib_exc.NotAcceptable, self.client.show_exported_zonefile,
             zone_export['id'], headers={'Accept': 'image/jpeg'})
         self.assertEqual(406, e.resp.status,
                          "Failed, actual response code is:{0}"
diff --git a/designate_tempest_plugin/tests/api/v2/test_zones_imports.py b/designate_tempest_plugin/tests/api/v2/test_zones_imports.py
index 86cf45e..6fcdae9 100644
--- a/designate_tempest_plugin/tests/api/v2/test_zones_imports.py
+++ b/designate_tempest_plugin/tests/api/v2/test_zones_imports.py
@@ -340,8 +340,7 @@
         self.assertIn(
             zone_import['id'], listed_zone_import_ids,
             "Failed, expected import ID:{} wasn't found in "
-            "listed import IDs".format(
-                zone_import['id'], listed_zone_import_ids))
+            "listed import IDs".format(zone_import['id']))
 
         # Test RBAC with x-auth-all-projects
         expected_allowed = ['os_admin']
diff --git a/devstack/README.rst b/devstack/README.rst
new file mode 100644
index 0000000..ee58314
--- /dev/null
+++ b/devstack/README.rst
@@ -0,0 +1,17 @@
+====================
+Enabling in Devstack
+====================
+
+1. Download DevStack::
+
+    git clone https://opendev.org/openstack/devstack.git
+    cd devstack
+
+2. Add this repo as an external repository::
+
+     > cat local.conf
+     [[local|localrc]]
+     enable_plugin designate-tempest-plugin https://opendev.org/openstack/designate-tempest-plugin
+
+3. run ``stack.sh``
+
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
new file mode 100644
index 0000000..288117e
--- /dev/null
+++ b/devstack/plugin.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+saveenv=$-
+set -e
+
+# install_designate_tempest_plugin
+function install_designate_tempest_plugin {
+    setup_dev_lib "designate-tempest-plugin"
+}
+
+function _configure_tempest {
+    if [ -n "$DESIGNATE_BIN_DIR" ]; then
+        iniset $TEMPEST_CONFIG dns_feature_enabled designate_manage_path ${DESIGNATE_BIN_DIR}/designate-manage
+    fi
+}
+
+if [[ "$1" == "stack" ]]; then
+    case "$2" in
+        install)
+            # Install dev library if the user explicitly requests it
+            # (INSTALL_TEMPEST=True)
+            if [[ "$(trueorfalse False INSTALL_TEMPEST)" == "True" ]]; then
+                echo_summary "Installing designate-tempest-plugin"
+                install_designate_tempest_plugin
+            fi
+            ;;
+        test-config)
+            echo_summary "Configuring tempest designate-manage"
+            _configure_tempest
+            ;;
+    esac
+fi
+
+if [[ $saveenv =~ e ]]; then
+    set -e
+else
+    set +e
+fi
diff --git a/devstack/settings b/devstack/settings
new file mode 100644
index 0000000..1a44c84
--- /dev/null
+++ b/devstack/settings
@@ -0,0 +1,3 @@
+GITREPO["designate-tempest-plugin"]=${DESIGNATE_TEMPEST_REPO:-${GIT_BASE}/openstack/designate-tempest-plugin.git}
+GITDIR["designate-tempest-plugin"]=$DEST/designate-tempest-plugin
+GITBRANCH["designate-tempest-plugin"]=${DESIGNATE_TEMPEST_REF:-master}
diff --git a/test-requirements.txt b/test-requirements.txt
index c0a7610..622077f 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,4 +3,4 @@
 # process, which may cause wedges in the gate later.
 
 # Hacking already pins down pep8/pycodestyle pyflakes and flake8
-hacking>=3.0.1,<3.1.0 # Apache-2.0
+hacking>=6.1.0,<6.2.0 # Apache-2.0
diff --git a/tools/pretty_flake8.py b/tools/pretty_flake8.py
index 5ae7efd..908dc0a 100755
--- a/tools/pretty_flake8.py
+++ b/tools/pretty_flake8.py
@@ -19,7 +19,7 @@
 from prettytable import PrettyTable
 
 PEP8_LINE = r'^((?P<file>.*):(?P<line>\d*):(?P<col>\d*):) ' \
-            '(?P<error>(?P<error_code>\w\d{1,3})(?P<error_desc>.*$))'
+            r'(?P<error>(?P<error_code>\w\d{1,3})(?P<error_desc>.*$))'
 
 HTML = True
 
diff --git a/tox.ini b/tox.ini
index 7a5148b..4f0539b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -100,8 +100,9 @@
   T108 = tempest.hacking.checks:no_hyphen_at_end_of_rand_name
   N322 = tempest.hacking.checks:no_mutable_default_args
   T109 = tempest.hacking.checks:no_testtools_skip_decorator
-  T110 = tempest.hacking.checks:get_resources_on_service_clients
-  T111 = tempest.hacking.checks:delete_resources_on_service_clients
+# TODO(johnsom) Re-enable these once tempest updates hacking to > 3.1.0
+#  T110 = tempest.hacking.checks:get_resources_on_service_clients
+#  T111 = tempest.hacking.checks:delete_resources_on_service_clients
   T112 = tempest.hacking.checks:dont_import_local_tempest_into_lib
   T113 = tempest.hacking.checks:dont_use_config_in_tempest_lib
   T114 = tempest.hacking.checks:use_rand_uuid_instead_of_uuid4