Merge "Add Tempest gate job for stable/2024.2"
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 20ace9e..33e75ff 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -454,6 +454,10 @@
 
   .. _2.86: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id79
 
+  * `2.96`_
+
+  .. _2.96: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-2024-1-caracal-and-2024-2-dalmatian
+
 * Volume
 
   * `3.3`_
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 475a99c..633d90e 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v41.0.0
    v40.0.0
    v39.0.0
    v38.0.0
diff --git a/releasenotes/source/v41.0.0.rst b/releasenotes/source/v41.0.0.rst
new file mode 100644
index 0000000..6d79c4c
--- /dev/null
+++ b/releasenotes/source/v41.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v41.0.0 Release Notes
+=====================
+
+.. release-notes:: 41.0.0 Release Notes
+   :version: 41.0.0
diff --git a/requirements.txt b/requirements.txt
index b0df18b..d2f13a5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,7 +13,7 @@
 oslo.log>=3.36.0 # Apache-2.0
 stestr>=1.0.0 # Apache-2.0
 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
-oslo.utils>=4.7.0 # Apache-2.0
+oslo.utils>=7.0.0 # Apache-2.0
 fixtures>=3.0.0 # Apache-2.0/BSD
 PyYAML>=3.12 # MIT
 python-subunit>=1.0.0 # Apache-2.0/BSD
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index c72b74e..e7e84d6 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -263,3 +263,22 @@
         servers = self.servers_client.list_servers(
             detail=True, **params)['servers']
         self.assertNotEmpty(servers)
+
+
+class ServersListShow296Test(base.BaseV2ComputeTest):
+    """Test compute server with microversion >= than 2.96
+
+    This test tests the Server APIs response schema for 2.96 microversion.
+    No specific assert or behaviour verification is needed.
+    """
+
+    min_microversion = '2.96'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('4eee1ffe-9e00-4c99-a431-0d3e0f323a8f')
+    def test_list_show_server_296(self):
+        server = self.create_test_server()
+        # Checking list API response schema.
+        self.servers_client.list_servers(detail=True)
+        # Checking show API response schema
+        self.servers_client.show_server(server['id'])
diff --git a/tempest/lib/api_schema/response/compute/v2_96/__init__.py b/tempest/lib/api_schema/response/compute/v2_96/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_96/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_96/servers.py b/tempest/lib/api_schema/response/compute/v2_96/servers.py
new file mode 100644
index 0000000..7036a11
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_96/servers.py
@@ -0,0 +1,62 @@
+#    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 copy
+
+from tempest.lib.api_schema.response.compute.v2_89 import servers as servers289
+
+
+###########################################################################
+#
+# 2.96:
+#
+# The attachment_id and bdm_uuid parameter is now returned in the response body
+# of the following calls:
+# The pinned_availability_zone parameter is now returned in the response body
+# of the following calls:
+#
+# - GET /servers/detail
+# - GET /servers/{server_id}
+###########################################################################
+
+get_server = copy.deepcopy(servers289.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update(
+        {'pinned_availability_zone': {'type': ['string', 'null']}})
+
+list_servers_detail = copy.deepcopy(servers289.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'properties'].update(
+        {'pinned_availability_zone': {'type': ['string', 'null']}})
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We
+# need to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.89***
+attach_volume = copy.deepcopy(servers289.attach_volume)
+show_volume_attachment = copy.deepcopy(servers289.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers289.list_volume_attachments)
+rebuild_server = copy.deepcopy(servers289.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers289.rebuild_server_with_admin_pass)
+update_server = copy.deepcopy(servers289.update_server)
+list_servers = copy.deepcopy(servers289.list_servers)
+show_server_diagnostics = copy.deepcopy(servers289.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers289.get_remote_consoles)
+list_tags = copy.deepcopy(servers289.list_tags)
+update_all_tags = copy.deepcopy(servers289.update_all_tags)
+delete_all_tags = copy.deepcopy(servers289.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers289.check_tag_existence)
+update_tag = copy.deepcopy(servers289.update_tag)
+delete_tag = copy.deepcopy(servers289.delete_tag)
+show_instance_action = copy.deepcopy(servers289.show_instance_action)
+create_backup = copy.deepcopy(servers289.create_backup)
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 8bdf98e..12fffdb 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -21,6 +21,7 @@
 from urllib import parse as urlparse
 
 from oslo_log import log as logging
+from oslo_utils import timeutils
 
 from tempest.lib import exceptions
 from tempest.lib.services.identity.v2 import token_client as json_v2id
@@ -419,8 +420,7 @@
     def is_expired(self, auth_data):
         _, access = auth_data
         expiry = self._parse_expiry_time(access['token']['expires'])
-        return (expiry - self.token_expiry_threshold <=
-                datetime.datetime.utcnow())
+        return (expiry - self.token_expiry_threshold <= timeutils.utcnow())
 
 
 class KeystoneV3AuthProvider(KeystoneAuthProvider):
@@ -595,8 +595,7 @@
     def is_expired(self, auth_data):
         _, access = auth_data
         expiry = self._parse_expiry_time(access['expires_at'])
-        return (expiry - self.token_expiry_threshold <=
-                datetime.datetime.utcnow())
+        return (expiry - self.token_expiry_threshold <= timeutils.utcnow())
 
 
 def is_identity_version_supported(identity_version):
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 1b93f91..e91c87a 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -45,6 +45,7 @@
 from tempest.lib.api_schema.response.compute.v2_8 import servers as schemav28
 from tempest.lib.api_schema.response.compute.v2_89 import servers as schemav289
 from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
+from tempest.lib.api_schema.response.compute.v2_96 import servers as schemav296
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
@@ -75,7 +76,8 @@
         {'min': '2.73', 'max': '2.74', 'schema': schemav273},
         {'min': '2.75', 'max': '2.78', 'schema': schemav275},
         {'min': '2.79', 'max': '2.88', 'schema': schemav279},
-        {'min': '2.89', 'max': None, 'schema': schemav289}]
+        {'min': '2.89', 'max': '2.95', 'schema': schemav289},
+        {'min': '2.96', 'max': None, 'schema': schemav296}]
 
     def __init__(self, auth_provider, service, region,
                  enable_instance_password=True, **kwargs):
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 3edb122..4e5ec48 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -17,6 +17,7 @@
 import datetime
 
 import fixtures
+from oslo_utils import timeutils
 import testtools
 
 from tempest.lib import auth
@@ -509,15 +510,15 @@
         self._test_base_url_helper(expected, filters, ('token', auth_data))
 
     def test_token_not_expired(self):
-        expiry_data = datetime.datetime.utcnow() + datetime.timedelta(days=1)
+        expiry_data = timeutils.utcnow() + datetime.timedelta(days=1)
         self._verify_expiry(expiry_data=expiry_data, should_be_expired=False)
 
     def test_token_expired(self):
-        expiry_data = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
+        expiry_data = timeutils.utcnow() - datetime.timedelta(hours=1)
         self._verify_expiry(expiry_data=expiry_data, should_be_expired=True)
 
     def test_token_not_expired_to_be_renewed(self):
-        expiry_data = (datetime.datetime.utcnow() +
+        expiry_data = (timeutils.utcnow() +
                        self.auth_provider.token_expiry_threshold / 2)
         self._verify_expiry(expiry_data=expiry_data, should_be_expired=True)
 
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index 8077308..56c65c0 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -430,6 +430,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-networking
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -449,6 +454,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
         # and job is broken up to wallaby branch due to the issue
@@ -489,6 +499,7 @@
             branches:
               - ^.*/2023.2
               - ^.*/2024.1
+              - ^.*/2024.2
               - master
         - tempest-integrated-compute
         # Do not run it on ussuri until below issue is fixed
@@ -505,6 +516,7 @@
             branches:
               - ^.*/2023.2
               - ^.*/2024.1
+              - ^.*/2024.2
               - master
         - tempest-integrated-compute
         - openstacksdk-functional-devstack:
@@ -542,6 +554,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-placement
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -561,6 +578,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
         # and job is broken up to wallaby branch due to the issue
@@ -593,6 +615,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-storage
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -611,6 +638,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-storage
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -637,6 +669,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-object-storage
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -655,6 +692,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-object-storage
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057