diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst index 17059e4abc..5263fddff5 100644 --- a/doc/source/microversion_testing.rst +++ b/doc/source/microversion_testing.rst @@ -217,3 +217,7 @@ Microversion tests implemented in Tempest * `2.20`_ .. _2.20: http://docs.openstack.org/developer/nova/api_microversion_history.html#id18 + + * `2.25`_ + + .. _2.25: http://docs.openstack.org/developer/nova/api_microversion_history.html#maximum-in-mitaka diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py index 94635ff571..dd7beaa11b 100644 --- a/tempest/api/compute/admin/test_live_migration.py +++ b/tempest/api/compute/admin/test_live_migration.py @@ -27,6 +27,8 @@ CONF = config.CONF class LiveBlockMigrationTestJSON(base.BaseV2ComputeAdminTest): _host_key = 'OS-EXT-SRV-ATTR:host' + max_microversion = '2.24' + block_migration = None @classmethod def skip_checks(cls): @@ -64,12 +66,16 @@ class LiveBlockMigrationTestJSON(base.BaseV2ComputeAdminTest): return self._get_server_details(server_id)[self._host_key] def _migrate_server_to(self, server_id, dest_host, volume_backed=False): - block_migration = (CONF.compute_feature_enabled. - block_migration_for_live_migration and - not volume_backed) + kwargs = dict() + block_migration = getattr(self, 'block_migration', None) + if self.block_migration is None: + kwargs['disk_over_commit'] = False + block_migration = (CONF.compute_feature_enabled. + block_migration_for_live_migration and + not volume_backed) body = self.admin_servers_client.live_migrate_server( server_id, host=dest_host, block_migration=block_migration, - disk_over_commit=False) + **kwargs) return body def _get_host_other_than(self, host): @@ -167,3 +173,9 @@ class LiveBlockMigrationTestJSON(base.BaseV2ComputeAdminTest): waiters.wait_for_server_status(self.servers_client, server_id, 'ACTIVE') self.assertEqual(target_host, self._get_host_for_server(server_id)) + + +class LiveAutoBlockMigrationV225TestJSON(LiveBlockMigrationTestJSON): + min_microversion = '2.25' + max_microversion = 'latest' + block_migration = 'auto' diff --git a/tempest/lib/api_schema/response/compute/v2_16/__init__.py b/tempest/lib/api_schema/response/compute/v2_16/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tempest/lib/api_schema/response/compute/v2_16/servers.py b/tempest/lib/api_schema/response/compute/v2_16/servers.py new file mode 100644 index 0000000000..6868110382 --- /dev/null +++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py @@ -0,0 +1,160 @@ +# Copyright 2014 NEC 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_1 import parameter_types +from tempest.lib.api_schema.response.compute.v2_9 import servers + +# Compute microversion 2.16: +# 1. New attributes in 'server' dict. +# 'host_status' + +server_detail = { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'name': {'type': 'string'}, + 'status': {'type': 'string'}, + 'image': {'oneOf': [ + {'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'links': parameter_types.links + }, + 'additionalProperties': False, + 'required': ['id', 'links']}, + {'type': ['string', 'null']} + ]}, + 'flavor': { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'links': parameter_types.links + }, + 'additionalProperties': False, + 'required': ['id', 'links'] + }, + 'fault': { + 'type': 'object', + 'properties': { + 'code': {'type': 'integer'}, + 'created': {'type': 'string'}, + 'message': {'type': 'string'}, + 'details': {'type': 'string'}, + }, + 'additionalProperties': False, + # NOTE(gmann): 'details' is not necessary to be present + # in the 'fault'. So it is not defined as 'required'. + 'required': ['code', 'created', 'message'] + }, + 'user_id': {'type': 'string'}, + 'tenant_id': {'type': 'string'}, + 'created': {'type': 'string'}, + 'updated': {'type': 'string'}, + 'progress': {'type': 'integer'}, + 'metadata': {'type': 'object'}, + 'links': parameter_types.links, + 'addresses': parameter_types.addresses, + 'hostId': {'type': 'string'}, + 'OS-DCF:diskConfig': {'type': 'string'}, + 'accessIPv4': parameter_types.access_ip_v4, + 'accessIPv6': parameter_types.access_ip_v6, + 'key_name': {'type': ['string', 'null']}, + 'security_groups': {'type': 'array'}, + 'OS-SRV-USG:launched_at': {'type': ['string', 'null']}, + 'OS-SRV-USG:terminated_at': {'type': ['string', 'null']}, + 'OS-EXT-AZ:availability_zone': {'type': 'string'}, + 'OS-EXT-STS:task_state': {'type': ['string', 'null']}, + 'OS-EXT-STS:vm_state': {'type': 'string'}, + 'OS-EXT-STS:power_state': {'type': 'integer'}, + 'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'}, + 'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']}, + 'config_drive': {'type': 'string'}, + 'os-extended-volumes:volumes_attached': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'delete_on_termination': {'type': 'boolean'} + }, + 'additionalProperties': False, + }, + }, + 'OS-EXT-SRV-ATTR:reservation_id': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:launch_index': {'type': 'integer'}, + 'OS-EXT-SRV-ATTR:kernel_id': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:ramdisk_id': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:hostname': {'type': 'string'}, + 'OS-EXT-SRV-ATTR:root_device_name': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:user_data': {'type': ['string', 'null']}, + 'locked': {'type': 'boolean'}, + # NOTE(gmann): new attributes in version 2.16 + 'host_status': {'type': 'string'} + }, + 'additionalProperties': False, + # NOTE(gmann): 'progress' attribute is present in the response + # only when server's status is one of the progress statuses + # ("ACTIVE","BUILD", "REBUILD", "RESIZE","VERIFY_RESIZE") + # 'fault' attribute is present in the response + # only when server's status is one of the "ERROR", "DELETED". + # OS-DCF:diskConfig and accessIPv4/v6 are API + # extensions, and some environments return a response + # without these attributes.So these are not defined as 'required'. + 'required': ['id', 'name', 'status', 'image', 'flavor', + 'user_id', 'tenant_id', 'created', 'updated', + 'metadata', 'links', 'addresses', 'hostId'] +} + +server_detail['properties']['addresses']['patternProperties'][ + '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({ + 'OS-EXT-IPS:type': {'type': 'string'}, + 'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address}) +# NOTE(gmann)dd: Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr +# attributes in server address. Those are API extension, +# and some environments return a response without +# these attributes. So they are not 'required'. + +get_server = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'server': server_detail + }, + 'additionalProperties': False, + 'required': ['server'] + } +} + +list_servers_detail = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'servers': { + 'type': 'array', + 'items': server_detail + }, + 'servers_links': parameter_types.links + }, + 'additionalProperties': False, + # NOTE(gmann): servers_links attribute is not necessary to be + # present always So it is not 'required'. + 'required': ['servers'] + } +} + +list_servers = copy.deepcopy(servers.list_servers) diff --git a/tempest/lib/api_schema/response/compute/v2_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py index 883839e3b8..05cc32c5be 100644 --- a/tempest/lib/api_schema/response/compute/v2_19/servers.py +++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py @@ -15,15 +15,18 @@ import copy from tempest.lib.api_schema.response.compute.v2_1 import servers as serversv21 -from tempest.lib.api_schema.response.compute.v2_9 import servers as serversv29 +from tempest.lib.api_schema.response.compute.v2_16 import servers \ + as serversv216 -get_server = copy.deepcopy(serversv29.get_server) +list_servers = copy.deepcopy(serversv216.list_servers) + +get_server = copy.deepcopy(serversv216.get_server) get_server['response_body']['properties']['server'][ 'properties'].update({'description': {'type': ['string', 'null']}}) get_server['response_body']['properties']['server'][ 'required'].append('description') -list_servers_detail = copy.deepcopy(serversv29.list_servers_detail) +list_servers_detail = copy.deepcopy(serversv216.list_servers_detail) list_servers_detail['response_body']['properties']['servers']['items'][ 'properties'].update({'description': {'type': ['string', 'null']}}) list_servers_detail['response_body']['properties']['servers']['items'][ diff --git a/tempest/lib/api_schema/response/compute/v2_23/__init__.py b/tempest/lib/api_schema/response/compute/v2_23/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tempest/lib/api_schema/response/compute/v2_23/migrations.py b/tempest/lib/api_schema/response/compute/v2_23/migrations.py new file mode 100644 index 0000000000..3cd0f6ec15 --- /dev/null +++ b/tempest/lib/api_schema/response/compute/v2_23/migrations.py @@ -0,0 +1,62 @@ +# Copyright 2014 NEC 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. + +from tempest.lib.api_schema.response.compute.v2_1 import parameter_types + +# Compute microversion 2.23: +# New attributes in 'migrations' list. +# 'migration_type' +# 'links' + +list_migrations = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'migrations': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'id': {'type': 'integer'}, + 'status': {'type': ['string', 'null']}, + 'instance_uuid': {'type': ['string', 'null']}, + 'source_node': {'type': ['string', 'null']}, + 'source_compute': {'type': ['string', 'null']}, + 'dest_node': {'type': ['string', 'null']}, + 'dest_compute': {'type': ['string', 'null']}, + 'dest_host': {'type': ['string', 'null']}, + 'old_instance_type_id': {'type': ['integer', 'null']}, + 'new_instance_type_id': {'type': ['integer', 'null']}, + 'created_at': {'type': 'string'}, + 'updated_at': {'type': ['string', 'null']}, + # New attributes in version 2.23 + 'migration_type': {'type': ['string', 'null']}, + 'links': parameter_types.links + }, + 'additionalProperties': False, + 'required': [ + 'id', 'status', 'instance_uuid', 'source_node', + 'source_compute', 'dest_node', 'dest_compute', + 'dest_host', 'old_instance_type_id', + 'new_instance_type_id', 'created_at', 'updated_at', + 'migration_type' + ] + } + } + }, + 'additionalProperties': False, + 'required': ['migrations'] + } +} diff --git a/tempest/lib/api_schema/response/compute/v2_3/__init__.py b/tempest/lib/api_schema/response/compute/v2_3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tempest/lib/api_schema/response/compute/v2_3/servers.py b/tempest/lib/api_schema/response/compute/v2_3/servers.py new file mode 100644 index 0000000000..ee16333c4b --- /dev/null +++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py @@ -0,0 +1,166 @@ +# Copyright 2014 NEC 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_1 import parameter_types +from tempest.lib.api_schema.response.compute.v2_1 import servers + +# Compute microversion 2.3: +# 1. New attributes in 'os-extended-volumes:volumes_attached' dict. +# 'delete_on_termination' +# 2. New attributes in 'server' dict. +# 'OS-EXT-SRV-ATTR:reservation_id' +# 'OS-EXT-SRV-ATTR:launch_index' +# 'OS-EXT-SRV-ATTR:kernel_id' +# 'OS-EXT-SRV-ATTR:ramdisk_id' +# 'OS-EXT-SRV-ATTR:hostname' +# 'OS-EXT-SRV-ATTR:root_device_name' +# 'OS-EXT-SRV-ATTR:user_data' + +server_detail = { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'name': {'type': 'string'}, + 'status': {'type': 'string'}, + 'image': {'oneOf': [ + {'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'links': parameter_types.links + }, + 'additionalProperties': False, + 'required': ['id', 'links']}, + {'type': ['string', 'null']} + ]}, + 'flavor': { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'links': parameter_types.links + }, + 'additionalProperties': False, + 'required': ['id', 'links'] + }, + 'fault': { + 'type': 'object', + 'properties': { + 'code': {'type': 'integer'}, + 'created': {'type': 'string'}, + 'message': {'type': 'string'}, + 'details': {'type': 'string'}, + }, + 'additionalProperties': False, + # NOTE(gmann): 'details' is not necessary to be present + # in the 'fault'. So it is not defined as 'required'. + 'required': ['code', 'created', 'message'] + }, + 'user_id': {'type': 'string'}, + 'tenant_id': {'type': 'string'}, + 'created': {'type': 'string'}, + 'updated': {'type': 'string'}, + 'progress': {'type': 'integer'}, + 'metadata': {'type': 'object'}, + 'links': parameter_types.links, + 'addresses': parameter_types.addresses, + 'hostId': {'type': 'string'}, + 'OS-DCF:diskConfig': {'type': 'string'}, + 'accessIPv4': parameter_types.access_ip_v4, + 'accessIPv6': parameter_types.access_ip_v6, + 'key_name': {'type': ['string', 'null']}, + 'security_groups': {'type': 'array'}, + 'OS-SRV-USG:launched_at': {'type': ['string', 'null']}, + 'OS-SRV-USG:terminated_at': {'type': ['string', 'null']}, + 'OS-EXT-AZ:availability_zone': {'type': 'string'}, + 'OS-EXT-STS:task_state': {'type': ['string', 'null']}, + 'OS-EXT-STS:vm_state': {'type': 'string'}, + 'OS-EXT-STS:power_state': {'type': 'integer'}, + 'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'}, + 'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']}, + 'config_drive': {'type': 'string'}, + # NOTE(gmann): new attributes in version 2.3 + 'os-extended-volumes:volumes_attached': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'id': {'type': 'string'}, + 'delete_on_termination': {'type': 'boolean'} + }, + 'additionalProperties': False, + }, + }, + 'OS-EXT-SRV-ATTR:reservation_id': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:launch_index': {'type': 'integer'}, + 'OS-EXT-SRV-ATTR:kernel_id': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:ramdisk_id': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:hostname': {'type': 'string'}, + 'OS-EXT-SRV-ATTR:root_device_name': {'type': ['string', 'null']}, + 'OS-EXT-SRV-ATTR:user_data': {'type': ['string', 'null']}, + }, + 'additionalProperties': False, + # NOTE(gmann): 'progress' attribute is present in the response + # only when server's status is one of the progress statuses + # ("ACTIVE","BUILD", "REBUILD", "RESIZE","VERIFY_RESIZE") + # 'fault' attribute is present in the response + # only when server's status is one of the "ERROR", "DELETED". + # OS-DCF:diskConfig and accessIPv4/v6 are API + # extensions, and some environments return a response + # without these attributes.So these are not defined as 'required'. + 'required': ['id', 'name', 'status', 'image', 'flavor', + 'user_id', 'tenant_id', 'created', 'updated', + 'metadata', 'links', 'addresses', 'hostId'] +} + +server_detail['properties']['addresses']['patternProperties'][ + '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({ + 'OS-EXT-IPS:type': {'type': 'string'}, + 'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address}) +# NOTE(gmann)dd: Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr +# attributes in server address. Those are API extension, +# and some environments return a response without +# these attributes. So they are not 'required'. + +get_server = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'server': server_detail + }, + 'additionalProperties': False, + 'required': ['server'] + } +} + +list_servers_detail = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'servers': { + 'type': 'array', + 'items': server_detail + }, + 'servers_links': parameter_types.links + }, + 'additionalProperties': False, + # NOTE(gmann): servers_links attribute is not necessary to be + # present always So it is not 'required'. + 'required': ['servers'] + } +} + +list_servers = copy.deepcopy(servers.list_servers) diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py index e9b724984f..470190c2c7 100644 --- a/tempest/lib/api_schema/response/compute/v2_9/servers.py +++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py @@ -14,7 +14,9 @@ import copy -from tempest.lib.api_schema.response.compute.v2_1 import servers +from tempest.lib.api_schema.response.compute.v2_3 import servers + +list_servers = copy.deepcopy(servers.list_servers) get_server = copy.deepcopy(servers.get_server) get_server['response_body']['properties']['server'][ diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py index 62246d3828..c3bdba742b 100644 --- a/tempest/lib/services/compute/migrations_client.py +++ b/tempest/lib/services/compute/migrations_client.py @@ -16,11 +16,16 @@ from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest.lib.api_schema.response.compute.v2_1 import migrations as schema +from tempest.lib.api_schema.response.compute.v2_23 import migrations \ + as schemav223 from tempest.lib.common import rest_client from tempest.lib.services.compute import base_compute_client class MigrationsClient(base_compute_client.BaseComputeClient): + schema_versions_info = [ + {'min': None, 'max': '2.22', 'schema': schema}, + {'min': '2.23', 'max': None, 'schema': schemav223}] def list_migrations(self, **params): """List all migrations. @@ -35,5 +40,6 @@ class MigrationsClient(base_compute_client.BaseComputeClient): resp, body = self.get(url) body = json.loads(body) + schema = self.get_schema(self.schema_versions_info) self.validate_response(schema.list_migrations, resp, body) return rest_client.ResponseBody(resp, body) diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py index 0d31ac792b..9444e20699 100644 --- a/tempest/lib/services/compute/servers_client.py +++ b/tempest/lib/services/compute/servers_client.py @@ -20,7 +20,9 @@ from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest.lib.api_schema.response.compute.v2_1 import servers as schema +from tempest.lib.api_schema.response.compute.v2_16 import servers as schemav216 from tempest.lib.api_schema.response.compute.v2_19 import servers as schemav219 +from tempest.lib.api_schema.response.compute.v2_3 import servers as schemav23 from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29 from tempest.lib.common import rest_client from tempest.lib.services.compute import base_compute_client @@ -28,8 +30,10 @@ from tempest.lib.services.compute import base_compute_client class ServersClient(base_compute_client.BaseComputeClient): schema_versions_info = [ - {'min': None, 'max': '2.8', 'schema': schema}, - {'min': '2.9', 'max': '2.18', 'schema': schemav29}, + {'min': None, 'max': '2.2', 'schema': schema}, + {'min': '2.3', 'max': '2.8', 'schema': schemav23}, + {'min': '2.9', 'max': '2.15', 'schema': schemav29}, + {'min': '2.16', 'max': '2.18', 'schema': schemav216}, {'min': '2.19', 'max': None, 'schema': schemav219}] def __init__(self, auth_provider, service, region,