Merge "Add host filtering for test_aggregate_add_existent_host and test_aggregate_remove_host_as_user tests"
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 33e75ff..e58753b 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -458,6 +458,14 @@
.. _2.96: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-2024-1-caracal-and-2024-2-dalmatian
+ * `2.98`_
+
+ .. _2.98: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#microversion-2-98
+
+ * `2.100`_
+
+ .. _2.100: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#microversion-2-100
+
* Volume
* `3.3`_
diff --git a/doc/source/supported_version.rst b/doc/source/supported_version.rst
index 0adfebd..3ffa68a 100644
--- a/doc/source/supported_version.rst
+++ b/doc/source/supported_version.rst
@@ -9,6 +9,7 @@
Tempest master supports the below OpenStack Releases:
+* 2025.1
* 2024.2
* 2024.1
* 2023.2
diff --git a/releasenotes/notes/change-default-disk-format-0d5230cbb19e3d44.yaml b/releasenotes/notes/change-default-disk-format-0d5230cbb19e3d44.yaml
new file mode 100644
index 0000000..57c5319
--- /dev/null
+++ b/releasenotes/notes/change-default-disk-format-0d5230cbb19e3d44.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+ - |
+ The default value for ``[volume] disk_format``, which specifies the
+ disk format of the image in a copy a volume to image operation,
+ is changed from ``raw`` to ``[raw, qcow2]`` for which the type of
+ the config option also needed to change from ``string`` type to
+ ``list`` type i.e. it accepts multiple values now.
diff --git a/releasenotes/notes/deprecate-spice-rdp-console-config-f2af173552axfb72.yaml b/releasenotes/notes/deprecate-spice-rdp-console-config-f2af173552axfb72.yaml
index 58b161f..313b276 100644
--- a/releasenotes/notes/deprecate-spice-rdp-console-config-f2af173552axfb72.yaml
+++ b/releasenotes/notes/deprecate-spice-rdp-console-config-f2af173552axfb72.yaml
@@ -1,6 +1,10 @@
---
deprecations:
- |
- The config options ``CONF.compute.spice_console`` and ``CONF.compute.rdp_console``
- are deprecated because test cases using them are removed.
- We can add them back when adding the test cases again.
+ The config option ``CONF.compute.rdp_console``
+ is deprecated because test cases using it have been removed.
+ We can add it back when adding the test cases again.
+ - |
+ The config option ``CONF.compute.spice_console`` was previously listed as
+ deprecated, but is now back in active use to support the testing of SPICE consoles
+ in Nova.
diff --git a/releasenotes/notes/tempest-2024-2-release-e706f62c7e841bd0.yaml b/releasenotes/notes/tempest-2024-2-release-e706f62c7e841bd0.yaml
new file mode 100644
index 0000000..86af60c
--- /dev/null
+++ b/releasenotes/notes/tempest-2024-2-release-e706f62c7e841bd0.yaml
@@ -0,0 +1,17 @@
+---
+prelude: >
+ This release is to tag Tempest for OpenStack 2025.1 release.
+ This release marks the start of 2025.1 release support in Tempest.
+ After this release, Tempest will support below OpenStack Releases:
+
+ * 2025.1
+ * 2024.2
+ * 2024.1
+ * 2023.2
+
+ Current development of Tempest is for OpenStack 2025.2 development
+ cycle. Every Tempest commit is also tested against master during
+ the 2025.2 cycle. However, this does not necessarily mean that using
+ Tempest as of this tag will work against a 2025.2 (or future release)
+ cloud.
+ To be on safe side, use this tag to test the OpenStack 2025.1 release.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 633d90e..058f65f 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,8 @@
:maxdepth: 1
unreleased
+ v43.0.0
+ v42.0.0
v41.0.0
v40.0.0
v39.0.0
diff --git a/releasenotes/source/v42.0.0.rst b/releasenotes/source/v42.0.0.rst
new file mode 100644
index 0000000..ffc375d
--- /dev/null
+++ b/releasenotes/source/v42.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v42.0.0 Release Notes
+=====================
+
+.. release-notes:: 42.0.0 Release Notes
+ :version: 42.0.0
diff --git a/releasenotes/source/v43.0.0.rst b/releasenotes/source/v43.0.0.rst
new file mode 100644
index 0000000..073cd5c
--- /dev/null
+++ b/releasenotes/source/v43.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v43.0.0 Release Notes
+=====================
+
+.. release-notes:: 43.0.0 Release Notes
+ :version: 43.0.0
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index f52d4c0..c933c80 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -65,6 +65,18 @@
self.s1_id,
flavor_ref['id'])
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('7fcadfab-bd6a-4753-8db7-4a51e51aade9')
+ def test_restore_server_invalid_state(self):
+ """Restore-deleting a server not in 'soft-delete' state should fail
+
+ We can restore a soft deleted server, but can't restore a server that
+ is not in 'soft-delete' state.
+ """
+ self.assertRaises(lib_exc.Conflict,
+ self.client.restore_soft_deleted_server,
+ self.s1_id)
+
@decorators.idempotent_id('7368a427-2f26-4ad9-9ba9-911a0ec2b0db')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize not available.')
diff --git a/tempest/api/compute/admin/test_spice.py b/tempest/api/compute/admin/test_spice.py
new file mode 100644
index 0000000..f09012d
--- /dev/null
+++ b/tempest/api/compute/admin/test_spice.py
@@ -0,0 +1,153 @@
+# All Rights Reserved.
+#
+# 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 socket
+import struct
+import urllib.parse as urlparse
+
+from tempest.api.compute import base
+from tempest import config
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class SpiceDirectConsoleTestJSON(base.BaseV2ComputeAdminTest):
+ """Test the spice-direct console"""
+
+ create_default_network = True
+
+ min_microversion = '2.99'
+ max_microversion = 'latest'
+
+ # SPICE client protocol constants
+ magic = b'REDQ'
+ major = 2
+ minor = 2
+ main_channel = 1
+ common_caps = 11 # AuthSelection, AuthSpice, MiniHeader
+ channel_caps = 9 # SemiSeamlessMigrate, SeamlessMigrate
+
+ @classmethod
+ def skip_checks(cls):
+ super().skip_checks()
+ if not CONF.compute_feature_enabled.spice_console:
+ raise cls.skipException('SPICE console feature is disabled.')
+
+ def tearDown(self):
+ super().tearDown()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
+
+ @classmethod
+ def setup_clients(cls):
+ super().setup_clients()
+ cls.client = cls.servers_client
+
+ @classmethod
+ def resource_setup(cls):
+ super().resource_setup()
+ cls.server = cls.create_test_server(wait_until="ACTIVE")
+
+ @decorators.idempotent_id('80f4460d-1a06-403c-9e93-cf434c70be05')
+ def test_spice_direct(self):
+ """Test accessing spice-direct console of server"""
+
+ # Request a spice-direct console and validate the result. Any user can
+ # do this.
+ body = self.servers_client.get_remote_console(
+ self.server['id'], console_type='spice-direct', protocol='spice')
+
+ console_url = body['remote_console']['url']
+ parts = urlparse.urlparse(console_url)
+ qparams = urlparse.parse_qs(parts.query)
+ self.assertIn('token', qparams)
+ self.assertNotEmpty(qparams['token'])
+ self.assertEqual(1, len(qparams['token']))
+
+ self.assertEqual('spice', body['remote_console']['protocol'])
+ self.assertEqual('spice-direct', body['remote_console']['type'])
+
+ # For reasons best know to the python developers, the qparams values
+ # are lists as documented at
+ # https://docs.python.org/3/library/urllib.parse.html
+ token = qparams['token'][0]
+
+ # Turn that console token into hypervisor connection details. Only
+ # admins can do this because its expected that the request is coming
+ # from a proxy and we don't want to expose intimate hypervisor details
+ # to all users.
+ body = self.admin_servers_client.get_console_auth_token_details(
+ token)
+
+ console = body['console']
+ self.assertEqual(self.server['id'], console['instance_uuid'])
+ self.assertIn('port', console)
+ self.assertIn('tls_port', console)
+ self.assertIsNone(console['internal_access_path'])
+
+ # Connect to the specified non-TLS port and verify we get back
+ # a SPICE protocol greeting
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect((console['host'], console['port']))
+
+ # Send a client greeting
+ #
+ # ---- SpiceLinkMess ----
+ # 4s UINT32 magic value, must be REDQ
+ # I UINT32 major_version, must be 2
+ # I UINT32 minor_version, must be 2
+ # I UINT32 size number of bytes following this field to the end
+ # of this message.
+ # I UINT32 connection_id. In case of a new session (i.e., channel
+ # type is SPICE_CHANNEL_MAIN) this field is set to zero,
+ # and in response the server will allocate session id
+ # and will send it via the SpiceLinkReply message. In
+ # case of all other channel types, this field will be
+ # equal to the allocated session id.
+ # B UINT8 channel_type, we use main
+ # B UINT8 channel_id to connect to
+ # I UINT32 num_common_caps number of common client channel
+ # capabilities words
+ # I UINT32 num_channel_caps number of specific client channel
+ # capabilities words
+ # I UINT32 caps_offset location of the start of the capabilities
+ # vector given by the bytes offset from the “size”
+ # member (i.e., from the address of the “connection_id”
+ # member).
+ # ... capabilities
+ sock.sendall(struct.pack(
+ '<4sIIIIBBIIIII', self.magic, self.major, self.minor, 42 - 16,
+ 0, self.main_channel, 0, 1, 1, 18, self.common_caps,
+ self.channel_caps))
+
+ # ---- SpiceLinkReply ----
+ # 4s UINT32 magic value, must be equal to SPICE_MAGIC
+ # I UINT32 major_version, must be equal to SPICE_VERSION_MAJOR
+ # I UINT32 minor_version, must be equal to SPICE_VERSION_MINOR
+ # I UINT32 size number of bytes following this field to the end
+ # of this message.
+ # I UINT32 error code
+ # ...
+ buffered = sock.recv(20)
+ self.assertIsNotNone(buffered)
+ self.assertEqual(20, len(buffered))
+
+ magic, major, minor, _, error = struct.unpack_from('<4sIIII', buffered)
+ self.assertEqual(b'REDQ', magic)
+ self.assertEqual(2, major)
+ self.assertEqual(2, minor)
+ self.assertEqual(0, error)
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index e7e84d6..ea3a710 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -276,9 +276,63 @@
max_microversion = 'latest'
@decorators.idempotent_id('4eee1ffe-9e00-4c99-a431-0d3e0f323a8f')
- def test_list_show_server_296(self):
- server = self.create_test_server()
+ def test_list_show_update_rebuild_server_296(self):
+ server = self.create_test_server(wait_until='ACTIVE')
# Checking list API response schema.
self.servers_client.list_servers(detail=True)
# Checking show API response schema
self.servers_client.show_server(server['id'])
+ # Checking update API response schema
+ self.servers_client.update_server(server['id'])
+ # Check rebuild API response schema
+ self.servers_client.rebuild_server(server['id'], self.image_ref_alt)
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'ACTIVE')
+
+
+class ServersListShow298Test(base.BaseV2ComputeTest):
+ """Test compute server with microversion >= 2.98"""
+
+ min_microversion = '2.98'
+ max_microversion = 'latest'
+
+ @decorators.idempotent_id('3981e496-3bf7-4015-b807-63ffee7c520c')
+ def test_list_show_update_rebuild_server_298(self):
+ server = self.create_test_server(wait_until='ACTIVE')
+ # Check list details API response schema
+ self.servers_client.list_servers(detail=True)
+ # Check show API response schema
+ self.servers_client.show_server(server['id'])
+ # Checking update API response schema
+ self.servers_client.update_server(server['id'])
+ # Check rebuild API response schema
+ self.servers_client.rebuild_server(server['id'], self.image_ref_alt)
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'ACTIVE')
+
+
+class ServersListShow2100Test(base.BaseV2ComputeTest):
+ """Test compute server with microversion >= than 2.100
+
+ This test tests the Server APIs response schema for 2.100 microversion.
+ No specific assert or behaviour verification is needed.
+ """
+
+ min_microversion = '2.100'
+ max_microversion = 'latest'
+
+ @decorators.idempotent_id('2c3a8270-e6f7-4400-af0f-db003c117e48')
+ def test_list_show_rebuild_update_server_2100(self):
+ server = self.create_test_server(wait_until='ACTIVE')
+ # Checking list API response schema.
+ self.servers_client.list_servers(detail=True)
+ # Checking show API response schema
+ self.servers_client.show_server(server['id'])
+ # Checking update API response schema
+ self.servers_client.update_server(server['id'])
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'ACTIVE')
+ # Check rebuild API response schema
+ self.servers_client.rebuild_server(server['id'], self.image_ref_alt)
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'ACTIVE')
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 22fe54d..fa40629 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -472,18 +472,6 @@
self.client.restore_soft_deleted_server,
nonexistent_server)
- @decorators.attr(type=['negative'])
- @decorators.idempotent_id('7fcadfab-bd6a-4753-8db7-4a51e51aade9')
- def test_restore_server_invalid_state(self):
- """Restore-deleting a server not in 'soft-delete' state should fail
-
- We can restore a soft deleted server, but can't restore a server that
- is not in 'soft-delete' state.
- """
- self.assertRaises(lib_exc.Conflict,
- self.client.restore_soft_deleted_server,
- self.server_id)
-
@decorators.idempotent_id('abca56e2-a892-48ea-b5e5-e07e69774816')
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 8cf44be..8b2bc69 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -109,29 +109,37 @@
# it is shared with the other tests. After it is uploaded in Glance,
# there is no way to delete it from Cinder, so we delete it from Glance
# using the Glance images_client and from Cinder via tearDownClass.
- image_name = data_utils.rand_name(self.__class__.__name__ + '-Image',
- prefix=CONF.resource_name_prefix)
- body = self.volumes_client.upload_volume(
- self.volume['id'], image_name=image_name,
- disk_format=CONF.volume.disk_format)['os-volume_upload_image']
- image_id = body["image_id"]
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.images_client.delete_image,
- image_id)
- waiters.wait_for_image_status(self.images_client, image_id, 'active')
- # This is required for the optimized upload volume path.
- # New location APIs are async so we need to wait for the location
- # import task to complete.
- # This should work with old location API since we don't fail if there
- # are no tasks for the image
- waiters.wait_for_image_tasks_status(self.images_client,
- image_id, 'success')
- waiters.wait_for_volume_resource_status(self.volumes_client,
- self.volume['id'], 'available')
+ # NOTE: This looks really strange to loop through the disk formats
+ # but similar implementation is done in test_volume_bootable test.
+ # Also there is no trace of ddt usage in tempest so this looks like
+ # the only way.
+ for disk_format in CONF.volume.disk_format:
+ image_name = data_utils.rand_name(
+ self.__class__.__name__ + '-Image',
+ prefix=CONF.resource_name_prefix)
+ body = self.volumes_client.upload_volume(
+ self.volume['id'], image_name=image_name,
+ disk_format=disk_format)['os-volume_upload_image']
+ image_id = body["image_id"]
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.images_client.delete_image,
+ image_id)
+ waiters.wait_for_image_status(self.images_client, image_id,
+ 'active')
+ # This is required for the optimized upload volume path.
+ # New location APIs are async so we need to wait for the location
+ # import task to complete.
+ # This should work with old location API since we don't fail if
+ # there are no tasks for the image
+ waiters.wait_for_image_tasks_status(self.images_client,
+ image_id, 'success')
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ self.volume['id'],
+ 'available')
- image_info = self.images_client.show_image(image_id)
- self.assertEqual(image_name, image_info['name'])
- self.assertEqual(CONF.volume.disk_format, image_info['disk_format'])
+ image_info = self.images_client.show_image(image_id)
+ self.assertEqual(image_name, image_info['name'])
+ self.assertEqual(disk_format, image_info['disk_format'])
@decorators.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
def test_reserve_unreserve_volume(self):
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 9b79f38..640441a 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -18,7 +18,6 @@
from tempest.api.volume import base
from tempest.common import utils
-from tempest.common import waiters
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -38,10 +37,7 @@
# Create a volume
kwargs['name'] = v_name
kwargs['metadata'] = metadata
- volume = self.volumes_client.create_volume(**kwargs)['volume']
- self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
- waiters.wait_for_volume_resource_status(self.volumes_client,
- volume['id'], 'available')
+ volume = self.create_volume(wait_until='available', **kwargs)
self.assertEqual(volume['name'], v_name,
"The created volume name is not equal "
"to the requested name")
@@ -103,11 +99,7 @@
params = {'description': new_v_desc,
'availability_zone': volume['availability_zone'],
'size': CONF.volume.volume_size}
- new_volume = self.volumes_client.create_volume(**params)['volume']
- self.addCleanup(self.delete_volume, self.volumes_client,
- new_volume['id'])
- waiters.wait_for_volume_resource_status(self.volumes_client,
- new_volume['id'], 'available')
+ new_volume = self.create_volume(wait_until='available', **params)
params = {'name': volume['name'],
'description': volume['description']}
diff --git a/tempest/config.py b/tempest/config.py
index 7719720..9c288ff 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -540,12 +540,8 @@
'be same as nova.conf: vnc.enabled'),
cfg.BoolOpt('spice_console',
default=False,
- help='Enable Spice console. This configuration value should '
- 'be same as nova.conf: spice.enabled',
- deprecated_for_removal=True,
- deprecated_reason="This config option is not being used "
- "in Tempest, we can add it back when "
- "adding the test cases."),
+ help='Enable SPICE console. This configuration value should '
+ 'be same as nova.conf: spice.enabled'),
cfg.BoolOpt('serial_console',
default=False,
help='Enable serial console. This configuration value '
@@ -586,10 +582,13 @@
default=True,
help='Enable special configuration drive with metadata.'),
cfg.ListOpt('scheduler_enabled_filters',
- default=["AvailabilityZoneFilter", "ComputeFilter",
- "ComputeCapabilitiesFilter", "ImagePropertiesFilter",
- "ServerGroupAntiAffinityFilter",
- "ServerGroupAffinityFilter"],
+ default=[
+ "ComputeFilter",
+ "ComputeCapabilitiesFilter",
+ "ImagePropertiesFilter",
+ "ServerGroupAntiAffinityFilter",
+ "ServerGroupAffinityFilter",
+ ],
help="A list of enabled filters that Nova will accept as "
"hints to the scheduler when creating a server. If the "
"default value is overridden in nova.conf by the test "
@@ -1026,9 +1025,9 @@
cfg.StrOpt('vendor_name',
default='Open Source',
help='Backend vendor to target when creating volume types'),
- cfg.StrOpt('disk_format',
- default='raw',
- help='Disk format to use when copying a volume to image'),
+ cfg.ListOpt('disk_format',
+ default=['raw', 'qcow2'],
+ help='Disk format to use when copying a volume to image'),
cfg.IntOpt('volume_size',
default=1,
help='Default size in GB for volumes created by volumes tests'),
diff --git a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
index b36c9d6..d5c9b95 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
@@ -127,3 +127,16 @@
{'type': 'null'}
]
}
+
+server_id = {
+ 'type': 'string', 'format': 'uuid'
+}
+
+name = {
+ # NOTE: Nova v2.1 API contains some 'name' parameters such
+ # as server, flavor, aggregate and so on. They are
+ # stored in the DB and Nova specific parameters.
+ # This definition is used for all their parameters.
+ 'type': 'string', 'minLength': 1, 'maxLength': 255,
+ 'format': 'name'
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_100/__init__.py b/tempest/lib/api_schema/response/compute/v2_100/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_100/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_100/servers.py b/tempest/lib/api_schema/response/compute/v2_100/servers.py
new file mode 100644
index 0000000..8721387
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_100/servers.py
@@ -0,0 +1,129 @@
+# 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_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_99 import servers as servers299
+
+###########################################################################
+#
+# 2.100:
+#
+# The scheduler_hints parameter is now returned in the response body
+# of the following calls:
+# - GET /servers/detail
+# - GET /servers/{server_id}
+# - POST /server/{server_id}/action (rebuild)
+# - PUT /servers/{server_id}
+#
+###########################################################################
+
+_hints = {
+ 'type': 'object',
+ 'properties': {
+ 'group': {
+ 'type': 'string',
+ 'format': 'uuid'
+ },
+ 'different_host': {
+ # NOTE: The value of 'different_host' is the set of server
+ # uuids where a new server is scheduled on a different host.
+ # A user can specify one server as string parameter and should
+ # specify multiple servers as array parameter instead.
+ 'oneOf': [
+ {
+ 'type': 'string',
+ 'format': 'uuid'
+ },
+ {
+ 'type': 'array',
+ 'items': parameter_types.server_id
+ }
+ ]
+ },
+ 'same_host': {
+ # NOTE: The value of 'same_host' is the set of server
+ # uuids where a new server is scheduled on the same host.
+ 'type': ['string', 'array'],
+ 'items': parameter_types.server_id
+ },
+ 'query': {
+ # NOTE: The value of 'query' is converted to dict data with
+ # jsonutils.loads() and used for filtering hosts.
+ 'type': ['string', 'object'],
+ },
+ # NOTE: The value of 'target_cell' is the cell name what cell
+ # a new server is scheduled on.
+ 'target_cell': parameter_types.name,
+ 'different_cell': {
+ 'type': ['string', 'array'],
+ 'items': {
+ 'type': 'string'
+ }
+ },
+ 'build_near_host_ip': parameter_types.ip_address,
+ 'cidr': {
+ 'type': 'string',
+ 'pattern': '^/[0-9a-f.:]+$'
+ },
+ },
+ # NOTE: As this Mail:
+ # http://lists.openstack.org/pipermail/openstack-dev/2015-June/067996.html
+ # pointed out the limit the scheduler-hints in the API is problematic. So
+ # relax it.
+ 'additionalProperties': True
+}
+
+get_server = copy.deepcopy(servers299.get_server)
+get_server['response_body']['properties']['server'][
+ 'properties'].update({'scheduler_hints': _hints})
+get_server['response_body']['properties']['server'][
+ 'required'].append('scheduler_hints')
+
+list_servers_detail = copy.deepcopy(servers299.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'properties'].update({'scheduler_hints': _hints})
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'required'].append('scheduler_hints')
+
+rebuild_server = copy.deepcopy(servers299.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+ 'properties'].update({'scheduler_hints': _hints})
+rebuild_server['response_body']['properties']['server'][
+ 'required'].append('scheduler_hints')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers299.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'properties'].update({'scheduler_hints': _hints})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'required'].append('scheduler_hints')
+
+update_server = copy.deepcopy(servers299.update_server)
+update_server['response_body']['properties']['server'][
+ 'properties'].update({'scheduler_hints': _hints})
+update_server['response_body']['properties']['server'][
+ 'required'].append('scheduler_hints')
+
+# 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.99***
+attach_volume = copy.deepcopy(servers299.attach_volume)
+show_volume_attachment = copy.deepcopy(servers299.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers299.list_volume_attachments)
+list_servers = copy.deepcopy(servers299.list_servers)
+show_server_diagnostics = copy.deepcopy(servers299.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers299.get_remote_consoles)
+show_instance_action = copy.deepcopy(servers299.show_instance_action)
+create_backup = copy.deepcopy(servers299.create_backup)
diff --git a/tempest/lib/api_schema/response/compute/v2_6/servers.py b/tempest/lib/api_schema/response/compute/v2_6/servers.py
index e6b2c32..05ab616 100644
--- a/tempest/lib/api_schema/response/compute/v2_6/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_6/servers.py
@@ -46,7 +46,8 @@
'properties': {
'protocol': {'enum': ['vnc', 'rdp', 'serial', 'spice']},
'type': {'enum': ['novnc', 'xvpvnc', 'rdp-html5',
- 'spice-html5', 'serial']},
+ 'spice-html5',
+ 'serial']},
'url': {
'type': 'string',
'format': 'uri'
diff --git a/tempest/lib/api_schema/response/compute/v2_96/servers.py b/tempest/lib/api_schema/response/compute/v2_96/servers.py
index 7036a11..8a4ed9f 100644
--- a/tempest/lib/api_schema/response/compute/v2_96/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_96/servers.py
@@ -26,17 +26,45 @@
#
# - GET /servers/detail
# - GET /servers/{server_id}
+# - PUT /servers/{server_id}
+# - POST /servers/{server_id}/action (rebuild)
###########################################################################
get_server = copy.deepcopy(servers289.get_server)
get_server['response_body']['properties']['server'][
'properties'].update(
{'pinned_availability_zone': {'type': ['string', 'null']}})
+get_server['response_body']['properties']['server'][
+ 'required'].append('pinned_availability_zone')
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']}})
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'required'].append('pinned_availability_zone')
+
+update_server = copy.deepcopy(servers289.update_server)
+update_server['response_body']['properties']['server'][
+ 'properties'].update(
+ {'pinned_availability_zone': {'type': ['string', 'null']}})
+update_server['response_body']['properties']['server'][
+ 'required'].append('pinned_availability_zone')
+
+rebuild_server = copy.deepcopy(servers289.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+ 'properties'].update(
+ {'pinned_availability_zone': {'type': ['string', 'null']}})
+rebuild_server['response_body']['properties']['server'][
+ 'required'].append('pinned_availability_zone')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers289.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'properties'].update(
+ {'pinned_availability_zone': {'type': ['string', 'null']}})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'required'].append('pinned_availability_zone')
# 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
@@ -45,10 +73,6 @@
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)
diff --git a/tempest/lib/api_schema/response/compute/v2_98/__init__.py b/tempest/lib/api_schema/response/compute/v2_98/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_98/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_98/servers.py b/tempest/lib/api_schema/response/compute/v2_98/servers.py
new file mode 100644
index 0000000..2fca3eb
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_98/servers.py
@@ -0,0 +1,85 @@
+# 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_96 import servers as servers296
+
+
+###########################################################################
+#
+# 2.98:
+#
+# The image properties parameter is now returned in the response body of the
+# following calls:
+#
+# - GET /servers/detail
+# - GET /servers/{server_id}
+# - PUT /servers/{server_id}
+# - POST /servers/{server_id}/action (rebuild)
+#
+###########################################################################
+
+image_properties = {
+ 'type': 'object',
+ 'patternProperties': {
+ '^[a-zA-Z0-9_:. ]{1,255}$': {
+ 'oneOf': [
+ {'type': 'string', 'maxLength': 255},
+ {'type': 'null'},
+ ]
+ },
+ },
+ 'additionalProperties': False,
+}
+
+get_server = copy.deepcopy(servers296.get_server)
+get_server['response_body']['properties']['server']['properties'][
+ 'image']['oneOf'][0]['properties'].update({'properties': image_properties})
+
+list_servers_detail = copy.deepcopy(servers296.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'properties']['image']['oneOf'][0]['properties'].update(
+ {'properties': image_properties})
+
+update_server = copy.deepcopy(servers296.update_server)
+update_server['response_body']['properties']['server']['properties'][
+ 'image']['oneOf'][0]['properties'].update({'properties': image_properties})
+
+rebuild_server = copy.deepcopy(servers296.rebuild_server)
+rebuild_server['response_body']['properties']['server']['properties'][
+ 'image']['oneOf'][0]['properties'].update({'properties': image_properties})
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers296.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'properties']['image']['oneOf'][0]['properties'].update(
+ {'properties': image_properties})
+
+# 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.96***
+attach_volume = copy.deepcopy(servers296.attach_volume)
+show_volume_attachment = copy.deepcopy(servers296.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers296.list_volume_attachments)
+list_servers = copy.deepcopy(servers296.list_servers)
+show_server_diagnostics = copy.deepcopy(servers296.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers296.get_remote_consoles)
+list_tags = copy.deepcopy(servers296.list_tags)
+update_all_tags = copy.deepcopy(servers296.update_all_tags)
+delete_all_tags = copy.deepcopy(servers296.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers296.check_tag_existence)
+update_tag = copy.deepcopy(servers296.update_tag)
+delete_tag = copy.deepcopy(servers296.delete_tag)
+show_instance_action = copy.deepcopy(servers296.show_instance_action)
+create_backup = copy.deepcopy(servers296.create_backup)
diff --git a/tempest/lib/api_schema/response/compute/v2_99/__init__.py b/tempest/lib/api_schema/response/compute/v2_99/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_99/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_99/servers.py b/tempest/lib/api_schema/response/compute/v2_99/servers.py
new file mode 100644
index 0000000..e667321
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_99/servers.py
@@ -0,0 +1,78 @@
+# 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_98 import servers
+
+# NOTE: 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.98 ******
+list_servers = copy.deepcopy(servers.list_servers)
+get_server = copy.deepcopy(servers.get_server)
+list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
+show_instance_action = copy.deepcopy(servers.show_instance_action)
+create_backup = copy.deepcopy(servers.create_backup)
+
+console_auth_tokens = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'console': {
+ 'type': 'object',
+ 'properties': {
+ 'instance_uuid': {'type': 'string'},
+ 'host': {'type': 'string'},
+ 'port': {'type': 'integer'},
+ 'tls_port': {'type': ['integer', 'null']},
+ 'internal_access_path': {'type': ['string', 'null']}
+ }
+ }
+ }
+ }
+}
+
+get_remote_consoles = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'remote_console': {
+ 'type': 'object',
+ 'properties': {
+ 'protocol': {'enum': ['vnc', 'rdp', 'serial', 'spice']},
+ 'type': {'enum': ['novnc', 'xvpvnc', 'rdp-html5',
+ 'spice-html5', 'spice-direct',
+ 'serial']},
+ 'url': {
+ 'type': 'string',
+ 'format': 'uri'
+ }
+ },
+ 'additionalProperties': False,
+ 'required': ['protocol', 'type', 'url']
+ }
+ },
+ 'additionalProperties': False,
+ 'required': ['remote_console']
+ }
+}
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index e91c87a..4a607a3 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -46,6 +46,10 @@
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.api_schema.response.compute.v2_98 import servers as schemav298
+from tempest.lib.api_schema.response.compute.v2_99 import servers as schemav299
+from tempest.lib.api_schema.response.compute.v2_100 import \
+ servers as schemav2100 # noqa: H306
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
@@ -77,7 +81,11 @@
{'min': '2.75', 'max': '2.78', 'schema': schemav275},
{'min': '2.79', 'max': '2.88', 'schema': schemav279},
{'min': '2.89', 'max': '2.95', 'schema': schemav289},
- {'min': '2.96', 'max': None, 'schema': schemav296}]
+ {'min': '2.96', 'max': '2.97', 'schema': schemav296},
+ {'min': '2.98', 'max': '2.98', 'schema': schemav298},
+ {'min': '2.99', 'max': '2.99', 'schema': schemav299},
+ {'min': '2.100', 'max': None, 'schema': schemav2100},
+ ]
def __init__(self, auth_provider, service, region,
enable_instance_password=True, **kwargs):
@@ -680,6 +688,19 @@
self.validate_response(schema.get_remote_consoles, resp, body)
return rest_client.ResponseBody(resp, body)
+ def get_console_auth_token_details(self, token):
+ """Turn a console auth token into hypervisor connection details.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://docs.openstack.org/api-ref/compute/#show-console-connection-information
+ """
+ resp, body = self.get('/os-console-auth-tokens/%s' % token)
+ body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(schema.console_auth_tokens, resp, body)
+ return rest_client.ResponseBody(resp, body)
+
def rescue_server(self, server_id, **kwargs):
"""Rescue the provided server.
diff --git a/tempest/scenario/test_instances_with_cinder_volumes.py b/tempest/scenario/test_instances_with_cinder_volumes.py
index b2c0501..3d43e0a 100644
--- a/tempest/scenario/test_instances_with_cinder_volumes.py
+++ b/tempest/scenario/test_instances_with_cinder_volumes.py
@@ -12,6 +12,8 @@
# 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 time
+
from oslo_log import log as logging
from tempest.common import utils
@@ -52,10 +54,11 @@
available compute nodes, up to CONF.compute.min_compute_nodes.
Total number of volumes is equal to
compute nodes * len(volume_types_for_data_volume)
- 6. Attach volumes to the instances
- 7. Assign floating IP to all instances
- 8. Configure security group for ssh access to all instances
- 9. Confirm ssh access to all instances
+ 6. Assign floating IP to all instances
+ 7. Configure security group for ssh access to all instances
+ 8. Confirm ssh access to all instances
+ 9. Attach volumes to the instances; fixup device mapping if
+ required
10. Run write test to all volumes through ssh connection per
instance
11. Clean up the sources, an instance, volumes, keypair and image
@@ -143,8 +146,6 @@
start = 0
end = len(volume_types)
for server in servers:
- attached_volumes = []
-
# wait for server to become active
waiters.wait_for_server_status(self.servers_client,
server['id'], 'ACTIVE')
@@ -170,26 +171,18 @@
)
# attach volumes to the instances
+ attached_volumes = []
for volume in created_volumes[start:end]:
-
- # wait for volume to become available
- waiters.wait_for_volume_resource_status(
- self.volumes_client, volume['id'], 'available')
-
- attached_volume = self.nova_volume_attach(server, volume)
- attached_volumes.append(attached_volume)
+ attached_volume, actual_dev = self._attach_fixup(
+ server, volume)
+ attached_volumes.append((attached_volume, actual_dev))
LOG.debug("Attached volume %s to server %s",
attached_volume['id'], server['id'])
server_name = server['name'].split('-')[-1]
# run write test on all volumes
- for volume in attached_volumes:
-
- # dev name volume['attachments'][0]['device'][5:] is like
- # /dev/vdb, we need to remove /dev/ -> first 5 chars
- dev_name = volume['attachments'][0]['device'][5:]
-
+ for volume, dev_name in attached_volumes:
mount_path = f"/mnt/{server_name}"
timestamp_before = self.create_timestamp(
@@ -216,3 +209,49 @@
start += len(volume_types)
end += len(volume_types)
+
+ def _attach_fixup(self, server, volume):
+ """Attach a volume to the server and update the device key with the
+ device actually created inside the guest.
+ """
+ waiters.wait_for_volume_resource_status(
+ self.volumes_client, volume['id'], 'available')
+
+ list_blks = "lsblk --nodeps --noheadings --output NAME"
+
+ blks_before = set(self.linux_client.exec_command(
+ list_blks).strip().splitlines())
+
+ attached_volume = self.nova_volume_attach(server, volume)
+ # dev name volume['attachments'][0]['device'][5:] is like
+ # /dev/vdb, we need to remove /dev/ -> first 5 chars
+ dev_name = attached_volume['attachments'][0]['device'][5:]
+
+ retry = 0
+ actual_dev = None
+ blks_now = set()
+ while retry < 4 and not actual_dev:
+ try:
+ blks_now = set(self.linux_client.exec_command(
+ list_blks).strip().splitlines())
+ for blk_dev in (blks_now - blks_before):
+ serial = self.linux_client.exec_command(
+ f"cat /sys/block/{blk_dev}/serial")
+ if serial == volume['id'][:len(serial)]:
+ actual_dev = blk_dev
+ break
+ except exceptions.SSHExecCommandFailed:
+ retry += 1
+ time.sleep(2 ** retry)
+
+ if not actual_dev and len(blks_now - blks_before):
+ LOG.warning("Detected new devices in guest but could not match any"
+ f" of them with the volume {volume['id']}")
+
+ if actual_dev and dev_name != actual_dev:
+ LOG.info(
+ f"OpenStack mapping {volume['id']} to device {dev_name}" +
+ f" is actually {actual_dev} inside the guest")
+ dev_name = actual_dev
+
+ return attached_volume, dev_name
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index febc2f6..aaa39c9 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -36,6 +36,11 @@
if not CONF.service_available.cinder:
raise cls.skipException("Cinder is not available")
+ @classmethod
+ def setup_clients(cls):
+ super(TestVolumeBootPattern, cls).setup_clients()
+ cls.servers_client = cls.os_primary.servers_client
+
def _delete_server(self, server):
self.servers_client.delete_server(server['id'])
waiters.wait_for_server_termination(self.servers_client, server['id'])
@@ -133,6 +138,37 @@
server=server_from_snapshot)
self.assertEqual(timestamp, timestamp3)
+ @decorators.idempotent_id('e3f4f2fc-5c6a-4be6-9c54-aedfc0954da7')
+ @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+ 'Cinder volume snapshots are disabled')
+ @utils.services('compute', 'volume', 'image')
+ def test_bootable_volume_snapshot_stop_start_instance(self):
+ # Step 1: Create a bootable volume from an image
+ volume = self.create_volume_from_image()
+
+ # Step 2: Boot an instance from the created volume
+ instance = self.boot_instance_from_resource(
+ source_id=volume['id'],
+ source_type='volume',
+ wait_until='SSHABLE'
+ )
+
+ # Step 3: Stop the instance
+ self.servers_client.stop_server(instance['id'])
+ waiters.wait_for_server_status(self.servers_client, instance['id'],
+ 'SHUTOFF')
+
+ # Step 4: Create a snapshot of the bootable volume
+ self.create_volume_snapshot(volume['id'], force=True)
+
+ # Step 5: Start the instance and verify it returns to ACTIVE state
+ self.servers_client.start_server(instance['id'])
+ waiters.wait_for_server_status(self.servers_client, instance['id'],
+ 'ACTIVE')
+
+ # Step 6: Verify console log
+ self.log_console_output([instance])
+
@decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
@decorators.attr(type='slow')
@testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
diff --git a/tempest/serial_tests/api/compute/admin/test_server_affinity.py b/tempest/serial_tests/api/compute/admin/test_server_affinity.py
index 4400130..71738bc 100644
--- a/tempest/serial_tests/api/compute/admin/test_server_affinity.py
+++ b/tempest/serial_tests/api/compute/admin/test_server_affinity.py
@@ -71,7 +71,7 @@
body, servers = compute.create_test_server(
self.os_primary, networks='none', **kwargs)
for server in servers:
- self.addCleanup(self.servers_client.delete_server, server['id'])
+ self.addCleanup(self.delete_server, server['id'])
return body
def _create_server_group(self, **kwargs):
diff --git a/tempest/tests/cmd/test_cleanup.py b/tempest/tests/cmd/test_cleanup.py
index 3efc9bd..7f67328 100644
--- a/tempest/tests/cmd/test_cleanup.py
+++ b/tempest/tests/cmd/test_cleanup.py
@@ -23,7 +23,8 @@
def test_load_json_saved_state(self):
# instantiate "empty" TempestCleanup
- c = cleanup.TempestCleanup(None, None, 'test')
+ app = mock.Mock()
+ c = cleanup.TempestCleanup(app, None, 'test')
test_saved_json = 'tempest/tests/cmd/test_saved_state_json.json'
with open(test_saved_json, 'r') as f:
test_saved_json_content = json.load(f)
@@ -35,7 +36,8 @@
def test_load_json_resource_list(self):
# instantiate "empty" TempestCleanup
- c = cleanup.TempestCleanup(None, None, 'test')
+ app = mock.Mock()
+ c = cleanup.TempestCleanup(app, None, 'test')
test_resource_list = 'tempest/tests/cmd/test_resource_list.json'
with open(test_resource_list, 'r') as f:
test_resource_list_content = json.load(f)
@@ -49,7 +51,8 @@
@mock.patch('tempest.cmd.cleanup.TempestCleanup.init')
@mock.patch('tempest.cmd.cleanup.TempestCleanup._cleanup')
def test_take_action_got_exception(self, mock_cleanup, mock_init):
- c = cleanup.TempestCleanup(None, None, 'test')
+ app = mock.Mock()
+ c = cleanup.TempestCleanup(app, None, 'test')
c.GOT_EXCEPTIONS.append('exception')
mock_cleanup.return_value = True
mock_init.return_value = True
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index 47b7812..49f9ebc 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -455,10 +455,13 @@
- grenade-skip-level:
branches:
- ^.*/2024.1
- # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+ # on 2025.1(SLURP) grenade-skip-level-always is voting.
# which test stable/2024.1 to 2025.1 upgrade.
+ # As extra testing, we do run it voting on current master(even that is non SLURP).
+ # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
- grenade-skip-level-always:
branches:
+ - ^.*/2025.1
- master
- tempest-integrated-networking
# Do not run it on ussuri until below issue is fixed
@@ -479,10 +482,13 @@
- grenade-skip-level:
branches:
- ^.*/2024.1
- # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+ # on 2025.1(SLURP) grenade-skip-level-always is voting.
# which test stable/2024.1 to 2025.1 upgrade.
+ # As extra testing, we do run it voting on current master(even that is non SLURP).
+ # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
- grenade-skip-level-always:
branches:
+ - ^.*/2025.1
- master
# Do not run it on ussuri until below issue is fixed
# https://storyboard.openstack.org/#!/story/2010057
@@ -525,6 +531,7 @@
- ^.*/2023.2
- ^.*/2024.1
- ^.*/2024.2
+ - ^.*/2025.1
- master
- tempest-integrated-compute
# Do not run it on ussuri until below issue is fixed
@@ -542,6 +549,7 @@
- ^.*/2023.2
- ^.*/2024.1
- ^.*/2024.2
+ - ^.*/2025.1
- master
- tempest-integrated-compute
- openstacksdk-functional-devstack:
@@ -579,10 +587,13 @@
- grenade-skip-level:
branches:
- ^.*/2024.1
- # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+ # on 2025.1(SLURP) grenade-skip-level-always is voting.
# which test stable/2024.1 to 2025.1 upgrade.
+ # As extra testing, we do run it voting on current master(even that is non SLURP).
+ # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
- grenade-skip-level-always:
branches:
+ - ^.*/2025.1
- master
- tempest-integrated-placement
# Do not run it on ussuri until below issue is fixed
@@ -603,10 +614,13 @@
- grenade-skip-level:
branches:
- ^.*/2024.1
- # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+ # on 2025.1(SLURP) grenade-skip-level-always is voting.
# which test stable/2024.1 to 2025.1 upgrade.
+ # As extra testing, we do run it voting on current master(even that is non SLURP).
+ # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
- grenade-skip-level-always:
branches:
+ - ^.*/2025.1
- master
# Do not run it on ussuri until below issue is fixed
# https://storyboard.openstack.org/#!/story/2010057
@@ -640,10 +654,13 @@
- grenade-skip-level:
branches:
- ^.*/2024.1
- # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+ # on 2025.1(SLURP) grenade-skip-level-always is voting.
# which test stable/2024.1 to 2025.1 upgrade.
+ # As extra testing, we do run it voting on current master(even that is non SLURP).
+ # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
- grenade-skip-level-always:
branches:
+ - ^.*/2025.1
- master
- tempest-integrated-storage
# Do not run it on ussuri until below issue is fixed
@@ -663,10 +680,13 @@
- grenade-skip-level:
branches:
- ^.*/2024.1
- # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+ # on 2025.1(SLURP) grenade-skip-level-always is voting.
# which test stable/2024.1 to 2025.1 upgrade.
+ # As extra testing, we do run it voting on current master(even that is non SLURP).
+ # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
- grenade-skip-level-always:
branches:
+ - ^.*/2025.1
- master
- tempest-integrated-storage
# Do not run it on ussuri until below issue is fixed
@@ -694,10 +714,13 @@
- grenade-skip-level:
branches:
- ^.*/2024.1
- # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+ # on 2025.1(SLURP) grenade-skip-level-always is voting.
# which test stable/2024.1 to 2025.1 upgrade.
+ # As extra testing, we do run it voting on current master(even that is non SLURP).
+ # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
- grenade-skip-level-always:
branches:
+ - ^.*/2025.1
- master
- tempest-integrated-object-storage
# Do not run it on ussuri until below issue is fixed
@@ -717,10 +740,13 @@
- grenade-skip-level:
branches:
- ^.*/2024.1
- # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+ # on 2025.1(SLURP) grenade-skip-level-always is voting.
# which test stable/2024.1 to 2025.1 upgrade.
+ # As extra testing, we do run it voting on current master(even that is non SLURP).
+ # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
- grenade-skip-level-always:
branches:
+ - ^.*/2025.1
- master
- tempest-integrated-object-storage
# Do not run it on ussuri until below issue is fixed
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 2f21c2d..f044e79 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -45,7 +45,7 @@
# if things are working in latest and oldest it will work in between
# stable branches also. If anything is breaking we will be catching
# those in respective stable branch gate.
- - tempest-full-2024-2:
+ - tempest-full-2025-1:
irrelevant-files: *tempest-irrelevant-files
- tempest-full-2023-2:
irrelevant-files: *tempest-irrelevant-files
@@ -110,8 +110,7 @@
irrelevant-files: *tempest-irrelevant-files
- tempest-full-enforce-scope-new-defaults:
irrelevant-files: *tempest-irrelevant-files
- - devstack-plugin-ceph-tempest-py3:
- timeout: 9000
+ - nova-ceph-multistore:
irrelevant-files: *tempest-irrelevant-files
- neutron-ovs-grenade-multinode:
irrelevant-files: *tempest-irrelevant-files
@@ -156,8 +155,8 @@
irrelevant-files: *tempest-irrelevant-files
- tempest-full-enforce-scope-new-defaults:
irrelevant-files: *tempest-irrelevant-files
- #- devstack-plugin-ceph-tempest-py3:
- # irrelevant-files: *tempest-irrelevant-files
+ - nova-ceph-multistore:
+ irrelevant-files: *tempest-irrelevant-files
- nova-live-migration:
irrelevant-files: *tempest-irrelevant-files
- ironic-tempest-bios-ipmi-direct-tinyipa:
@@ -173,8 +172,6 @@
experimental:
jobs:
- nova-multi-cell
- - nova-ceph-multistore:
- irrelevant-files: *tempest-irrelevant-files
- tempest-with-latest-microversion
- tempest-stestr-master
- tempest-cinder-v2-api:
@@ -201,12 +198,15 @@
irrelevant-files: *tempest-irrelevant-files
periodic-stable:
jobs:
+ - tempest-full-2025-1
- tempest-full-2024-2
- tempest-full-2024-1
- tempest-full-2023-2
+ - tempest-slow-2025-1
- tempest-slow-2024-2
- tempest-slow-2024-1
- tempest-slow-2023-2
+ - tempest-full-2025-1-extra-tests
- tempest-full-2024-2-extra-tests
- tempest-full-2024-1-extra-tests
- tempest-full-2023-2-extra-tests
diff --git a/zuul.d/stable-jobs.yaml b/zuul.d/stable-jobs.yaml
index 5785ec6..6409ae3 100644
--- a/zuul.d/stable-jobs.yaml
+++ b/zuul.d/stable-jobs.yaml
@@ -1,5 +1,11 @@
# NOTE(gmann): This file includes all stable release jobs definition.
- job:
+ name: tempest-full-2025-1
+ parent: tempest-full-py3
+ nodeset: openstack-single-node-noble
+ override-checkout: stable/2025.1
+
+- job:
name: tempest-full-2024-2
parent: tempest-full-py3
nodeset: openstack-single-node-jammy
@@ -18,6 +24,12 @@
override-checkout: stable/2023.2
- job:
+ name: tempest-full-2025-1-extra-tests
+ parent: tempest-extra-tests
+ nodeset: openstack-single-node-noble
+ override-checkout: stable/2025.1
+
+- job:
name: tempest-full-2024-2-extra-tests
parent: tempest-extra-tests
nodeset: openstack-single-node-jammy
@@ -36,6 +48,12 @@
override-checkout: stable/2023.2
- job:
+ name: tempest-slow-2025-1
+ parent: tempest-slow-py3
+ nodeset: openstack-two-node-noble
+ override-checkout: stable/2025.1
+
+- job:
name: tempest-slow-2024-2
parent: tempest-slow-py3
nodeset: openstack-two-node-jammy