From dd8370dfaa4d9c05781b3610389a27f6a34bac3d Mon Sep 17 00:00:00 2001 From: Daniel Vincze Date: Mon, 9 Dec 2024 22:44:46 +0200 Subject: [PATCH] Fix export_info NIC IP address update This patch makes sure that exported instances never lose IP addressing information, even after replicating with instance off. --- coriolis/tasks/replica_tasks.py | 24 ++++++++ .../tests/tasks/data/test_nic_ips_update.yml | 61 +++++++++++++++++++ coriolis/tests/tasks/test_replica_tasks.py | 8 +++ 3 files changed, 93 insertions(+) create mode 100644 coriolis/tests/tasks/data/test_nic_ips_update.yml diff --git a/coriolis/tasks/replica_tasks.py b/coriolis/tasks/replica_tasks.py index bdc2382ef..bdeb88bfa 100644 --- a/coriolis/tasks/replica_tasks.py +++ b/coriolis/tasks/replica_tasks.py @@ -74,6 +74,28 @@ def _check_ensure_volumes_info_ordering(export_info, volumes_info): return ordered_volumes_info +def _preserve_old_export_info_nic_ips(old_export_info, new_export_info): + def _get_nic(nics_info, nic_id): + for nic_info in nics_info: + if nic_info['id'] == nic_id: + return nic_info + + old_nics_info = old_export_info.get('devices', {}).get('nics', []) + new_nics_info = new_export_info.get('devices', {}).get('nics', []) + + for new_info in new_nics_info: + old_info = _get_nic(old_nics_info, new_info['id']) + if old_info: + old_ips = old_info.get('ip_addresses', []) + new_ips = new_info.get('ip_addresses', []) + if not new_ips: + new_info['ip_addresses'] = old_ips + + +def _update_export_info(old_export_info, result_export_info): + _preserve_old_export_info_nic_ips(old_export_info, result_export_info) + + class GetInstanceInfoTask(base.TaskRunner): """ Task which gathers the export info for a VM. """ @@ -104,12 +126,14 @@ def _run(self, ctxt, instance, origin, destination, task_info, connection_info = base.get_connection_info(ctxt, origin) source_environment = task_info['source_environment'] + old_export_info = task_info.get('export_info', {}) export_info = provider.get_replica_instance_info( ctxt, connection_info, source_environment, instance) # Validate the output schemas.validate_value( export_info, schemas.CORIOLIS_VM_EXPORT_INFO_SCHEMA) + _update_export_info(old_export_info, export_info) return { 'export_info': export_info} diff --git a/coriolis/tests/tasks/data/test_nic_ips_update.yml b/coriolis/tests/tasks/data/test_nic_ips_update.yml new file mode 100644 index 000000000..e6ec7eb6f --- /dev/null +++ b/coriolis/tests/tasks/data/test_nic_ips_update.yml @@ -0,0 +1,61 @@ +# Provider receives first IP address +- old_export_info: + devices: + nics: [] + new_export_info: + devices: + nics: + - id: 1 + ip_addresses: ['10.0.0.1'] + expected_export_info: + devices: + nics: + - id: 1 + ip_addresses: ['10.0.0.1'] + +# Export NIC changes +- old_export_info: + devices: + nics: + - id: 1 + ip_addresses: ['10.0.0.1'] + new_export_info: + devices: + nics: + - id: 2 + ip_addresses: [] + expected_export_info: + devices: + nics: + - id: 2 + ip_addresses: [] + +# Export NIC doesn't report IP address (result keeps old one) +- old_export_info: + devices: + nics: + - id: 1 + ip_addresses: ['10.0.0.1'] + new_export_info: + devices: + nics: + - id: 1 + ip_addresses: [] + expected_export_info: + devices: + nics: + - id: 1 + ip_addresses: ['10.0.0.1'] + +# No NICs get reported +- old_export_info: + devices: + nics: + - id: 1 + ip_addresses: ['10.0.0.1'] + new_export_info: + devices: + nics: [] + expected_export_info: + devices: + nics: [] diff --git a/coriolis/tests/tasks/test_replica_tasks.py b/coriolis/tests/tasks/test_replica_tasks.py index b5f75a20f..8a5d6d548 100644 --- a/coriolis/tests/tasks/test_replica_tasks.py +++ b/coriolis/tests/tasks/test_replica_tasks.py @@ -53,6 +53,14 @@ def test__check_ensure_volumes_info_ordering( mock_sanitize.has_calls(expected_calls) self.assertEqual(result, expected_result) + @ddt.file_data("data/test_nic_ips_update.yml") + @ddt.unpack + def test__preserve_old_export_info_nic_ips( + self, old_export_info, new_export_info, expected_export_info): + replica_tasks._preserve_old_export_info_nic_ips( + old_export_info, new_export_info) + self.assertEqual(new_export_info, expected_export_info) + class GetInstanceInfoTaskTestCase(test_base.CoriolisBaseTestCase):