Merge "compute: Move device tagging tests to use wait_until=SSHABLE"
diff --git a/HACKING.rst b/HACKING.rst
index 95bcbb5..dc28e4e 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -26,6 +26,7 @@
- [T116] Unsupported 'message' Exception attribute in PY3
- [T117] Check negative tests have ``@decorators.attr(type=['negative'])``
applied.
+- [T118] LOG.warn is deprecated. Enforce use of LOG.warning.
It is recommended to use ``tox -eautopep8`` before submitting a patch.
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index ecf2930..ff6237e 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -418,6 +418,10 @@
.. _2.63: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id58
+ * `2.64`_
+
+ .. _2.64: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id59
+
* `2.70`_
.. _2.70: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id64
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index a110eb4..e16afaf 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -306,10 +306,18 @@
def create_test_server_group(cls, name="", policy=None):
if not name:
name = data_utils.rand_name(cls.__name__ + "-Server-Group")
- if policy is None:
- policy = ['affinity']
+ if cls.is_requested_microversion_compatible('2.63'):
+ policy = policy or ['affinity']
+ if not isinstance(policy, list):
+ policy = [policy]
+ kwargs = {'policies': policy}
+ else:
+ policy = policy or 'affinity'
+ if isinstance(policy, list):
+ policy = policy[0]
+ kwargs = {'policy': policy}
body = cls.server_groups_client.create_server_group(
- name=name, policies=policy)['server_group']
+ name=name, **kwargs)['server_group']
cls.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
cls.server_groups_client.delete_server_group,
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index 4c0d021..4811a7b 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -44,9 +44,21 @@
cls.client = cls.server_groups_client
@classmethod
+ def _set_policy(cls, policy):
+ if not cls.is_requested_microversion_compatible('2.63'):
+ return policy[0]
+ else:
+ return policy
+
+ @classmethod
def resource_setup(cls):
super(ServerGroupTestJSON, cls).resource_setup()
- cls.policy = ['affinity']
+ if cls.is_requested_microversion_compatible('2.63'):
+ cls.policy_field = 'policies'
+ cls.policy = ['affinity']
+ else:
+ cls.policy_field = 'policy'
+ cls.policy = 'affinity'
def setUp(self):
super(ServerGroupTestJSON, self).setUp()
@@ -61,9 +73,9 @@
def _create_server_group(self, name, policy):
# create the test server-group with given policy
- server_group = {'name': name, 'policies': policy}
+ server_group = {'name': name, self.policy_field: policy}
body = self.create_test_server_group(name, policy)
- for key in ['name', 'policies']:
+ for key in ['name', self.policy_field]:
self.assertEqual(server_group[key], body[key])
return body
@@ -88,7 +100,7 @@
@decorators.idempotent_id('3645a102-372f-4140-afad-13698d850d23')
def test_create_delete_server_group_with_anti_affinity_policy(self):
"""Test Create/Delete the server-group with anti-affinity policy"""
- policy = ['anti-affinity']
+ policy = self._set_policy(['anti-affinity'])
self._create_delete_server_group(policy)
@decorators.idempotent_id('154dc5a4-a2fe-44b5-b99e-f15806a4a113')
@@ -99,7 +111,7 @@
for _ in range(0, 2):
server_groups.append(self._create_server_group(server_group_name,
self.policy))
- for key in ['name', 'policies']:
+ for key in ['name', self.policy_field]:
self.assertEqual(server_groups[0][key], server_groups[1][key])
self.assertNotEqual(server_groups[0]['id'], server_groups[1]['id'])
@@ -134,3 +146,24 @@
server_group = (self.server_groups_client.show_server_group(
self.created_server_group['id'])['server_group'])
self.assertIn(server['id'], server_group['members'])
+
+
+class ServerGroup264TestJSON(base.BaseV2ComputeTest):
+ """These tests check for the server-group APIs 2.64 microversion.
+
+ This tests is only to verify the POST, GET server-groups APIs response
+ schema with 2.64 microversion
+ """
+ create_default_network = True
+ min_microversion = '2.64'
+
+ @decorators.idempotent_id('b52f09dd-2133-4037-9a5d-bdb260096a88')
+ def test_create_get_server_group(self):
+ # create, get the test server-group with given policy
+ server_group = self.create_test_server_group(
+ name='server-group', policy='affinity')
+ self.addCleanup(
+ self.server_groups_client.delete_server_group,
+ server_group['id'])
+ self.server_groups_client.list_server_groups()
+ self.server_groups_client.show_server_group(server_group['id'])
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 0db1ab1..421afd3 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -130,7 +130,7 @@
msg = ('Glance is available in the catalog, but no known version, '
'(v1.x or v2.x) of Glance could be found, so Glance should '
'be configured as not available')
- LOG.warn(msg)
+ LOG.warning(msg)
print_and_or_update('glance', 'service-available', False, update)
return
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index c1e6b2d..1c9c55b 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -318,3 +318,16 @@
" to all negative API tests"
)
_HAVE_NEGATIVE_DECORATOR = False
+
+
+@core.flake8ext
+def no_log_warn(logical_line):
+ """Disallow 'LOG.warn('
+
+ Use LOG.warning() instead of Deprecated LOG.warn().
+ https://docs.python.org/3/library/logging.html#logging.warning
+ """
+
+ msg = ("T118: LOG.warn is deprecated, please use LOG.warning!")
+ if "LOG.warn(" in logical_line:
+ yield (0, msg)
diff --git a/tempest/lib/api_schema/response/compute/v2_64/__init__.py b/tempest/lib/api_schema/response/compute/v2_64/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_64/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_64/server_groups.py b/tempest/lib/api_schema/response/compute/v2_64/server_groups.py
new file mode 100644
index 0000000..1402de5
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_64/server_groups.py
@@ -0,0 +1,56 @@
+# Copyright 2020 ZTE Corporation. 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_13 import server_groups as \
+ server_groupsv213
+
+# Compute microversion 2.64:
+# 1. change policies to policy in:
+# * GET /os-server-groups
+# * POST /os-server-groups
+# * GET /os-server-groups/{server_group_id}
+# 2. add rules in:
+# * GET /os-server-groups
+# * POST /os-server-groups
+# * GET /os-server-groups/{server_group_id}
+# 3. remove metadata from:
+# * GET /os-server-groups
+# * POST /os-server-groups
+# * GET /os-server-groups/{server_group_id}
+
+common_server_group = copy.deepcopy(server_groupsv213.common_server_group)
+common_server_group['properties']['policy'] = {'type': 'string'}
+common_server_group['properties']['rules'] = {'type': 'object'}
+common_server_group['properties'].pop('policies')
+common_server_group['properties'].pop('metadata')
+common_server_group['required'].append('policy')
+common_server_group['required'].append('rules')
+common_server_group['required'].remove('policies')
+common_server_group['required'].remove('metadata')
+
+create_show_server_group = copy.deepcopy(
+ server_groupsv213.create_show_server_group)
+create_show_server_group['response_body']['properties'][
+ 'server_group'] = common_server_group
+
+list_server_groups = copy.deepcopy(server_groupsv213.list_server_groups)
+list_server_groups['response_body']['properties']['server_groups'][
+ 'items'] = common_server_group
+
+# 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.
+delete_server_group = copy.deepcopy(server_groupsv213.delete_server_group)
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
index 89ad2d9..9895653 100644
--- a/tempest/lib/services/compute/server_groups_client.py
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -20,6 +20,8 @@
as schema
from tempest.lib.api_schema.response.compute.v2_13 import server_groups \
as schemav213
+from tempest.lib.api_schema.response.compute.v2_64 import server_groups \
+ as schemav264
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
@@ -28,7 +30,8 @@
schema_versions_info = [
{'min': None, 'max': '2.12', 'schema': schema},
- {'min': '2.13', 'max': None, 'schema': schemav213}]
+ {'min': '2.13', 'max': '2.63', 'schema': schemav213},
+ {'min': '2.64', 'max': None, 'schema': schemav264}]
def create_server_group(self, **kwargs):
"""Create the server group.
diff --git a/tempest/tests/lib/services/image/v2/test_schemas_client.py b/tempest/tests/lib/services/image/v2/test_schemas_client.py
index eef5b41..9fb249b 100644
--- a/tempest/tests/lib/services/image/v2/test_schemas_client.py
+++ b/tempest/tests/lib/services/image/v2/test_schemas_client.py
@@ -75,6 +75,293 @@
}
}
+ FAKE_SHOW_SCHEMA_IMAGE = {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "links": [
+ {
+ "href": "{self}",
+ "rel": "self"
+ },
+ {
+ "href": "{file}",
+ "rel": "enclosure"
+ },
+ {
+ "href": "{schema}",
+ "rel": "describedby"
+ }
+ ],
+ "name": "image",
+ "properties": {
+ "architecture": {
+ "description": "Operating system architecture as "
+ "specified in https://docs.openstack.org/"
+ "python-glanceclient/latest/cli"
+ "/property-keys.html",
+ "is_base": False,
+ "type": "string"
+ },
+ "checksum": {
+ "description": "md5 hash of image contents.",
+ "maxLength": 32,
+ "readOnly": True,
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "container_format": {
+ "description": "Format of the container",
+ "enum": [
+ None,
+ "ami",
+ "ari",
+ "aki",
+ "bare",
+ "ovf",
+ "ova",
+ "docker"
+ ],
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "created_at": {
+ "description": "Date and time of image registration",
+ "readOnly": True,
+ "type": "string"
+ },
+ "direct_url": {
+ "description": "URL to access the image file "
+ "kept in external store",
+ "readOnly": True,
+ "type": "string"
+ },
+ "disk_format": {
+ "description": "Format of the disk",
+ "enum": [
+ None,
+ "ami",
+ "ari",
+ "aki",
+ "vhd",
+ "vhdx",
+ "vmdk",
+ "raw",
+ "qcow2",
+ "vdi",
+ "iso",
+ "ploop"
+ ],
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "file": {
+ "description": "An image file url",
+ "readOnly": True,
+ "type": "string"
+ },
+ "id": {
+ "description": "An identifier for the image",
+ "pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F])"
+ "{4}-([0-9a-fA-F]){4}-([0-9a-fA-F])"
+ "{4}-([0-9a-fA-F]){12}$",
+ "type": "string"
+ },
+ "instance_uuid": {
+ "description": "Metadata which can be used to record which"
+ " instance this image is associated with. "
+ "(Informational only, does not create "
+ "an instance snapshot.)",
+ "is_base": False,
+ "type": "string"
+ },
+ "kernel_id": {
+ "description": "ID of image stored in Glance that should "
+ "be used as the kernel when booting an "
+ "AMI-style image.",
+ "is_base": False,
+ "pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-"
+ "([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-("
+ "[0-9a-fA-F]){12}$",
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "locations": {
+ "description": "A set of URLs to access the image file "
+ "kept in external store",
+ "items": {
+ "properties": {
+ "metadata": {
+ "type": "object"
+ },
+ "url": {
+ "maxLength": 255,
+ "type": "string"
+ }
+ },
+ "required": [
+ "url",
+ "metadata"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "min_disk": {
+ "description": "Amount of disk space (in GB) "
+ "required to boot image.",
+ "type": "integer"
+ },
+ "min_ram": {
+ "description": "Amount of ram (in MB) required "
+ "to boot image.",
+ "type": "integer"
+ },
+ "name": {
+ "description": "Descriptive name for the image",
+ "maxLength": 255,
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "os_distro": {
+ "description": "Common name of operating system distribution "
+ "as specified in https://docs.openstack.org/"
+ "python-glanceclient/latest/cli/"
+ "property-keys.html",
+ "is_base": False,
+ "type": "string"
+ },
+ "os_hash_algo": {
+ "description": "Algorithm to calculate the os_hash_value",
+ "maxLength": 64,
+ "readOnly": True,
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "os_hash_value": {
+ "description": "Hexdigest of the image contents "
+ "using the algorithm specified by "
+ "the os_hash_algo",
+ "maxLength": 128,
+ "readOnly": True,
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "os_hidden": {
+ "description": "If true, image will not appear in default"
+ " image list response.",
+ "type": "boolean"
+ },
+ "os_version": {
+ "description": "Operating system version as specified by "
+ "the distributor",
+ "is_base": False,
+ "type": "string"
+ },
+ "owner": {
+ "description": "Owner of the image",
+ "maxLength": 255,
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "protected": {
+ "description": "If true, image will not be deletable.",
+ "type": "boolean"
+ },
+ "ramdisk_id": {
+ "description": "ID of image stored in Glance that should"
+ " be used as the ramdisk when booting an "
+ "AMI-style image.",
+ "is_base": False,
+ "pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F])"
+ "{4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$",
+ "type": [
+ "null",
+ "string"
+ ]
+ },
+ "schema": {
+ "description": "An image schema url",
+ "readOnly": True,
+ "type": "string"
+ },
+ "self": {
+ "description": "An image self url",
+ "readOnly": True,
+ "type": "string"
+ },
+ "size": {
+ "description": "Size of image file in bytes",
+ "readOnly": True,
+ "type": [
+ "null",
+ "integer"
+ ]
+ },
+ "status": {
+ "description": "Status of the image",
+ "enum": [
+ "queued",
+ "saving",
+ "active",
+ "killed",
+ "deleted",
+ "pending_delete",
+ "deactivated",
+ "uploading",
+ "importing"
+ ],
+ "readOnly": True,
+ "type": "string"
+ },
+ "tags": {
+ "description": "List of strings related to the image",
+ "items": {
+ "maxLength": 255,
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "updated_at": {
+ "description": "Date and time of the last image modification",
+ "readOnly": True,
+ "type": "string"
+ },
+ "virtual_size": {
+ "description": "Virtual size of image in bytes",
+ "readOnly": True,
+ "type": [
+ "null",
+ "integer"
+ ]
+ },
+ "visibility": {
+ "description": "Scope of image accessibility",
+ "enum": [
+ "public",
+ "private"
+ ],
+ "type": "string"
+ }
+ }
+ }
+
def setUp(self):
super(TestSchemasClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -89,6 +376,22 @@
bytes_body,
schema="members")
+ def _test_show_schema_image(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_schema,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_SHOW_SCHEMA_IMAGE,
+ bytes_body,
+ schema="image")
+
+ def _test_show_schema_images(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_schema,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_SHOW_SCHEMA_IMAGE,
+ bytes_body,
+ schema="images")
+
def _test_show_schema(self, bytes_body=False):
self.check_service_client_function(
self.client.show_schema,
@@ -103,6 +406,18 @@
def test_show_schema_members_with_bytes_body(self):
self._test_show_schema_members(bytes_body=True)
+ def test_show_schema_image_with_str_body(self):
+ self._test_show_schema_image()
+
+ def test_show_schema_image_with_bytes_body(self):
+ self._test_show_schema_image(bytes_body=True)
+
+ def test_show_schema_images_with_str_body(self):
+ self._test_show_schema_images()
+
+ def test_show_schema_images_with_bytes_body(self):
+ self._test_show_schema_images(bytes_body=True)
+
def test_show_schema_with_str_body(self):
self._test_show_schema()
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index 7c31185..464e66a 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -240,3 +240,9 @@
with_other_decorators=True,
with_negative_decorator=False,
expected_success=False)
+
+ def test_no_log_warn(self):
+ self.assertFalse(list(checks.no_log_warn(
+ 'LOG.warning("LOG.warn is deprecated")')))
+ self.assertTrue(list(checks.no_log_warn(
+ 'LOG.warn("LOG.warn is deprecated")')))
diff --git a/tox.ini b/tox.ini
index 18f2aa6..b07fdaf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -369,6 +369,7 @@
T115 = checks:dont_put_admin_tests_on_nonadmin_path
T116 = checks:unsupported_exception_attribute_PY3
T117 = checks:negative_test_attribute_always_applied_to_negative_tests
+ T118 = checks:no_log_warn
paths =
./tempest/hacking
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index fad17dd..d35e25d 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -335,6 +335,8 @@
check:
jobs:
- grenade
+ - grenade-skip-level:
+ voting: false
- tempest-integrated-networking
- openstacksdk-functional-devstack
gate:
@@ -352,6 +354,8 @@
run on Nova gate only.
check:
jobs:
+ - grenade-skip-level:
+ voting: false
- tempest-integrated-compute
- tempest-integrated-compute-centos-8-stream
- openstacksdk-functional-devstack
@@ -371,6 +375,8 @@
check:
jobs:
- grenade
+ - grenade-skip-level:
+ voting: false
- tempest-integrated-placement
- openstacksdk-functional-devstack
gate:
@@ -389,6 +395,8 @@
check:
jobs:
- grenade
+ - grenade-skip-level:
+ voting: false
- tempest-integrated-storage
- openstacksdk-functional-devstack
gate:
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 731a72a..e62f24a 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -122,6 +122,8 @@
irrelevant-files: *tempest-irrelevant-files-2
- tempest-full-py3-centos-8-stream:
irrelevant-files: *tempest-irrelevant-files
+ - tempest-full-centos-9-stream:
+ irrelevant-files: *tempest-irrelevant-files
gate:
jobs:
- openstack-tox-pep8
diff --git a/zuul.d/tempest-specific.yaml b/zuul.d/tempest-specific.yaml
index 5b6b702..7d28e5c 100644
--- a/zuul.d/tempest-specific.yaml
+++ b/zuul.d/tempest-specific.yaml
@@ -83,6 +83,12 @@
configure_swap_size: 4096
- job:
+ name: tempest-full-centos-9-stream
+ parent: tempest-full-py3-centos-8-stream
+ voting: false
+ nodeset: devstack-single-node-centos-9-stream
+
+- job:
name: tempest-tox-plugin-sanity-check
parent: tox
description: |