diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 161810867ce..779f42793b8 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -84,6 +84,7 @@ from nova import objects from nova.objects import base as obj_base from nova.objects import instance as obj_instance +from nova.objects import migrate_data as migrate_data_obj from nova import paths from nova import rpc from nova import safe_utils @@ -5008,6 +5009,8 @@ def _do_check_can_live_migrate_destination(self, ctxt, instance, dest_check_data = self.driver.check_can_live_migrate_destination(ctxt, instance, src_compute_info, dst_compute_info, block_migration, disk_over_commit) + if isinstance(dest_check_data, migrate_data_obj.LiveMigrateData): + dest_check_data = dest_check_data.to_legacy_dict() migrate_data = {} try: migrate_data = self.compute_rpcapi.\ @@ -5039,9 +5042,12 @@ def check_can_live_migrate_source(self, ctxt, instance, dest_check_data): dest_check_data['is_volume_backed'] = is_volume_backed block_device_info = self._get_instance_block_device_info( ctxt, instance, refresh_conn_info=True) - return self.driver.check_can_live_migrate_source(ctxt, instance, - dest_check_data, - block_device_info) + result = self.driver.check_can_live_migrate_source(ctxt, instance, + dest_check_data, + block_device_info) + if isinstance(result, migrate_data_obj.LiveMigrateData): + result = result.to_legacy_dict() + return result @wrap_exception() @wrap_instance_event @@ -5072,6 +5078,9 @@ def pre_live_migration(self, context, instance, block_migration, disk, network_info, disk, migrate_data) + if isinstance(pre_live_migration_data, + migrate_data_obj.LiveMigrateData): + pre_live_migration_data = pre_live_migration_data.to_legacy_dict() # NOTE(tr3buchet): setup networks on destination host self.network_api.setup_networks_on_host(context, instance, diff --git a/nova/objects/migrate_data.py b/nova/objects/migrate_data.py new file mode 100644 index 00000000000..2f89f7f26c5 --- /dev/null +++ b/nova/objects/migrate_data.py @@ -0,0 +1,41 @@ +# Copyright 2015 Red Hat, Inc. +# +# 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 nova.objects import base as obj_base +from nova.objects import fields + + +@obj_base.NovaObjectRegistry.register_if(False) +class LiveMigrateData(obj_base.NovaObject): + fields = { + 'is_volume_backed': fields.BooleanField(), + 'migration': fields.ObjectField('Migration'), + } + + def to_legacy_dict(self, pre_migration_result=False): + legacy = {} + if self.obj_attr_is_set('is_volume_backed'): + legacy['is_volume_backed'] = self.is_volume_backed + if self.obj_attr_is_set('migration'): + legacy['migration'] = self.migration + if pre_migration_result: + legacy['pre_live_migration_result'] = {} + + return legacy + + def from_legacy_dict(self, legacy): + if 'is_volume_backed' in legacy: + self.is_volume_backed = legacy['is_volume_backed'] + if 'migration' in legacy: + self.migration = legacy['migration'] diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index 6a7155bc237..8c162faf647 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -42,6 +42,7 @@ from nova.network import model as network_model from nova import objects from nova.objects import block_device as block_device_obj +from nova.objects import migrate_data as migrate_data_obj from nova import test from nova.tests.unit.compute import fake_resource_tracker from nova.tests.unit import fake_block_device @@ -4264,3 +4265,44 @@ def test_max_concurrent_live_semaphore_negative(self): self.assertEqual(0, compute._live_migration_semaphore.balance) self.assertIsInstance(compute._live_migration_semaphore, compute_utils.UnlimitedSemaphore) + + def test_check_migrate_source_converts_object(self): + # NOTE(danms): Make sure that we legacy-ify any data objects + # the drivers give us back, until we're ready for them + data = migrate_data_obj.LiveMigrateData(is_volume_backed=False) + compute = manager.ComputeManager() + + @mock.patch.object(compute.driver, 'check_can_live_migrate_source') + @mock.patch.object(compute, '_get_instance_block_device_info') + @mock.patch.object(compute.compute_api, 'is_volume_backed_instance') + def _test(mock_ivbi, mock_gibdi, mock_cclms): + mock_cclms.return_value = data + self.assertIsInstance( + compute.check_can_live_migrate_source( + self.context, {'uuid': 'foo'}, {}), + dict) + + _test() + + def test_check_migrate_destination_converts_object(self): + # NOTE(danms): Make sure that we legacy-ify any data objects + # the drivers give us back, until we're ready for them + data = migrate_data_obj.LiveMigrateData(is_volume_backed=False) + inst = objects.Instance(id=1, uuid='foo', host='bar') + compute = manager.ComputeManager() + + @mock.patch.object(compute.driver, + 'check_can_live_migrate_destination') + @mock.patch.object(compute.compute_rpcapi, + 'check_can_live_migrate_source') + @mock.patch.object(compute, '_get_compute_info') + def _test(mock_gci, mock_cclms, mock_cclmd): + mock_gci.return_value = inst + mock_cclmd.return_value = data + mock_cclms.return_value = {} + result = compute.check_can_live_migrate_destination( + self.context, inst, False, False) + self.assertIsInstance(mock_cclms.call_args_list[0][0][2], dict) + self.assertIsInstance(result, dict) + + _test() diff --git a/nova/tests/unit/objects/test_migrate_data.py b/nova/tests/unit/objects/test_migrate_data.py new file mode 100644 index 00000000000..687c57cefbe --- /dev/null +++ b/nova/tests/unit/objects/test_migrate_data.py @@ -0,0 +1,53 @@ +# Copyright 2015 Red Hat, Inc. +# +# 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 nova import objects +from nova.objects import migrate_data +from nova.tests.unit.objects import test_objects + + +class _TestLiveMigrateData(object): + def test_to_legacy_dict(self): + obj = migrate_data.LiveMigrateData(is_volume_backed=False) + self.assertEqual({'is_volume_backed': False}, + obj.to_legacy_dict()) + + def test_from_legacy_dict(self): + obj = migrate_data.LiveMigrateData() + obj.from_legacy_dict({'is_volume_backed': False, 'ignore': 'foo'}) + self.assertEqual(False, obj.is_volume_backed) + + def test_from_legacy_dict_migration(self): + migration = objects.Migration() + obj = migrate_data.LiveMigrateData() + obj.from_legacy_dict({'is_volume_backed': False, 'ignore': 'foo', + 'migration': migration}) + self.assertEqual(False, obj.is_volume_backed) + self.assertIsInstance(obj.migration, objects.Migration) + + def test_legacy_with_pre_live_migration_result(self): + obj = migrate_data.LiveMigrateData(is_volume_backed=False) + self.assertEqual({'pre_live_migration_result': {}, + 'is_volume_backed': False}, + obj.to_legacy_dict(pre_migration_result=True)) + + +class TestLiveMigrateData(test_objects._LocalTest, + _TestLiveMigrateData): + pass + + +class TestRemoteLiveMigrateData(test_objects._RemoteTest, + _TestLiveMigrateData): + pass diff --git a/nova/virt/hyperv/livemigrationops.py b/nova/virt/hyperv/livemigrationops.py index 2c5b678ab89..4dc42f40e7f 100644 --- a/nova/virt/hyperv/livemigrationops.py +++ b/nova/virt/hyperv/livemigrationops.py @@ -24,6 +24,7 @@ from oslo_utils import excutils from nova.i18n import _ +from nova.objects import migrate_data as migrate_data_obj from nova.virt.hyperv import imagecache from nova.virt.hyperv import pathutils from nova.virt.hyperv import vmops @@ -116,7 +117,7 @@ def check_can_live_migrate_destination(self, ctxt, instance_ref, block_migration=False, disk_over_commit=False): LOG.debug("check_can_live_migrate_destination called", instance_ref) - return {} + return migrate_data_obj.LiveMigrateData() @check_os_version_requirement def check_can_live_migrate_destination_cleanup(self, ctxt,