diff --git a/nova/api/openstack/compute/schemas/server_migrations.py b/nova/api/openstack/compute/schemas/server_migrations.py index 03a9be4009..0a09f66820 100644 --- a/nova/api/openstack/compute/schemas/server_migrations.py +++ b/nova/api/openstack/compute/schemas/server_migrations.py @@ -13,6 +13,11 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + +from nova.api.validation import parameter_types + + force_complete = { 'type': 'object', 'properties': { @@ -41,3 +46,102 @@ show_query = { force_complete_response = { 'type': 'null', } + +_migration_response = { + 'type': 'object', + 'properties': { + 'created_at': {'type': 'string', 'format': 'date-time'}, + 'dest_compute': {'type': ['string', 'null']}, + 'dest_host': {'type': ['string', 'null']}, + 'dest_node': {'type': ['string', 'null']}, + 'disk_processed_bytes': {'type': ['integer', 'null'], 'minimum': 1}, + 'disk_remaining_bytes': {'type': ['integer', 'null'], 'minimum': 1}, + 'disk_total_bytes': {'type': ['integer', 'null'], 'minimum': 1}, + 'id': {'type': 'integer'}, + 'memory_processed_bytes': {'type': ['integer', 'null'], 'minimum': 1}, + 'memory_remaining_bytes': {'type': ['integer', 'null'], 'minimum': 1}, + 'memory_total_bytes': {'type': ['integer', 'null'], 'minimum': 1}, + 'server_uuid': {'type': 'string', 'format': 'uuid'}, + 'source_compute': {'type': ['string', 'null']}, + 'source_node': {'type': ['string', 'null']}, + 'status': {'type': 'string'}, + 'updated_at': {'type': 'string', 'format': 'date-time'}, + }, + 'required': [ + 'created_at', + 'dest_compute', + 'dest_host', + 'dest_node', + 'disk_processed_bytes', + 'disk_remaining_bytes', + 'disk_total_bytes', + 'id', + 'memory_processed_bytes', + 'memory_remaining_bytes', + 'memory_total_bytes', + 'server_uuid', + 'source_compute', + 'source_node', + 'status', + 'updated_at', + ], + 'additionalProperties': False, +} + +_migration_response_v259 = copy.deepcopy(_migration_response) +_migration_response_v259['properties'].update({ + 'uuid': {'type': 'string', 'format': 'uuid'}, +}) +_migration_response_v259['required'].append('uuid') + +_migration_response_v280 = copy.deepcopy(_migration_response_v259) +_migration_response_v280['properties'].update({ + 'project_id': parameter_types.project_id, + 'user_id': parameter_types.user_id, +}) +_migration_response_v280['required'].extend([ + 'project_id', 'user_id' +]) + +index_response_v223 = { + 'type': 'object', + 'properties': { + 'migrations': { + 'type': 'array', + 'items': _migration_response, + }, + }, + 'required': ['migrations'], + 'additionalProperties': False, +} + +index_response_v259 = copy.deepcopy(index_response_v223) +index_response_v259['properties']['migrations']['items'] = ( + _migration_response_v259 +) + +index_response_v280 = copy.deepcopy(index_response_v259) +index_response_v280['properties']['migrations']['items'] = ( + _migration_response_v280 +) + +show_response_v223 = { + 'type': 'object', + 'properties': { + 'migration': _migration_response, + }, + 'required': ['migration'], + 'additionalProperties': False, +} + +show_response_v259 = copy.deepcopy(show_response_v223) +show_response_v259['properties']['migration'] = ( + _migration_response_v259 +) + +show_response_v280 = copy.deepcopy(show_response_v259) +show_response_v280['properties']['migration'] = ( + _migration_response_v280 +) + +delete_response_v224 = {'type': 'null'} diff --git a/nova/api/openstack/compute/server_migrations.py b/nova/api/openstack/compute/server_migrations.py index fa687f6ea2..e81b0bb2c5 100644 --- a/nova/api/openstack/compute/server_migrations.py +++ b/nova/api/openstack/compute/server_migrations.py @@ -83,6 +83,7 @@ def output( return result +@validation.validated class ServerMigrationsController(wsgi.Controller): """The server migrations API controller for the OpenStack API.""" @@ -119,6 +120,9 @@ class ServerMigrationsController(wsgi.Controller): @wsgi.api_version("2.23") @wsgi.expected_errors(404) @validation.query_schema(schema.index_query) + @validation.response_body_schema(schema.index_response_v223, '2.23', '2.58') # noqa: E501 + @validation.response_body_schema(schema.index_response_v259, '2.59', '2.79') # noqa: E501 + @validation.response_body_schema(schema.index_response_v280, '2.80') def index(self, req, server_id): """Return all migrations of an instance in progress.""" context = req.environ['nova.context'] @@ -145,6 +149,9 @@ class ServerMigrationsController(wsgi.Controller): @wsgi.api_version("2.23") @wsgi.expected_errors(404) @validation.query_schema(schema.show_query) + @validation.response_body_schema(schema.show_response_v223, '2.23', '2.58') + @validation.response_body_schema(schema.show_response_v259, '2.59', '2.79') # noqa: E501 + @validation.response_body_schema(schema.show_response_v280, '2.80') def show(self, req, server_id, id): """Return the migration of an instance in progress by id.""" context = req.environ['nova.context'] @@ -184,6 +191,7 @@ class ServerMigrationsController(wsgi.Controller): @wsgi.api_version("2.24") @wsgi.response(202) @wsgi.expected_errors((400, 404, 409)) + @validation.response_body_schema(schema.delete_response_v224, '2.24') def delete(self, req, server_id, id): """Abort an in progress migration of an instance.""" context = req.environ['nova.context'] diff --git a/nova/tests/unit/policies/test_server_migrations.py b/nova/tests/unit/policies/test_server_migrations.py index dd9083fe30..c1aeb2dae3 100644 --- a/nova/tests/unit/policies/test_server_migrations.py +++ b/nova/tests/unit/policies/test_server_migrations.py @@ -92,8 +92,8 @@ class ServerMigrationsPolicyTest(base.BasePolicyTest): 'deleted': False, 'uuid': uuids.migration1, 'cross_cell_move': False, - 'user_id': None, - 'project_id': None + 'user_id': uuids.user_id, + 'project_id': uuids.project_id, }, ] @@ -140,13 +140,28 @@ class ServerMigrationsPolicyTest(base.BasePolicyTest): rule, self.controller.index, self.req, self.instance.uuid) - @mock.patch('nova.api.openstack.compute.server_migrations.output') @mock.patch('nova.compute.api.API.get_migration_by_id_and_instance') - def test_show_server_migrations_policy(self, mock_show, mock_output): + def test_show_server_migrations_policy(self, mock_get): rule_name = policies.POLICY_ROOT % 'show' - mock_show.return_value = objects.Migration( + mock_get.return_value = objects.Migration( + created_at=datetime.datetime(2024, 12, 21, 12, 21), + dest_compute='foo', + dest_host='foo', + dest_node='foo', + disk_processed=1, + disk_remaining=99, + disk_total=100, + id=123, + instance_uuid=uuids.server_id, + memory_processed=1, + memory_remaining=99, + memory_total=100, migration_type='live-migration', + source_compute='bar', + source_node='bar', status='running', + updated_at=datetime.datetime(2024, 12, 21, 12, 21), + uuid=uuids.migration_id, ) self.common_policy_auth(self.project_admin_authorized_contexts, rule_name, self.controller.show,