Merge "[libvirt]Add migration_inbound_addr"

This commit is contained in:
Zuul 2023-12-05 11:43:35 +00:00 committed by Gerrit Code Review
commit 33ab9c5d0e
5 changed files with 197 additions and 4 deletions

View File

@ -917,6 +917,28 @@ Related options:
* ``compute_driver`` (libvirt)
* ``virt_type`` (qemu)
"""),
cfg.StrOpt('migration_inbound_addr',
default='$my_ip',
help="""
The address used as the migration address for this host.
This option indicates the IP address, hostname, or FQDN which should be used as
the target for cold migration, resize, and evacuate traffic when moving to this
hypervisor. This metadata is then used by the source of the migration traffic
to construct the commands used to copy data (e.g. disk image) to the
destination.
An included "%s" is replaced with the hostname of the migration target
hypervisor.
Related options:
* ``my_ip``
* ``live_migration_inbound_addr``
"""),
]

View File

@ -0,0 +1,91 @@
# 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 unittest import mock
from oslo_config import cfg
from nova.tests.functional.libvirt import base
CONF = cfg.CONF
class LibvirtMigrationAddrTest(base.ServersTestBase):
ADMIN_API = True
def setUp(self):
super().setUp()
def _test_move_op(self, move_op, migration_inbound_addr=None):
if migration_inbound_addr:
CONF.set_default(
"migration_inbound_addr", migration_inbound_addr,
group="libvirt")
self.start_compute(hostname='compute1')
self.start_compute(hostname='compute2')
server = self._create_server(host='compute1', networks="none")
move_op(server)
migration = self._wait_for_migration_status(
server, ["finished", "done"])
if migration_inbound_addr:
self.assertEqual(migration['dest_host'], "compute2")
else:
self.assertEqual(migration['dest_host'], CONF.my_ip)
def _cold_migrate(self, server):
with mock.patch(
'nova.virt.libvirt.driver.LibvirtDriver' +
'.migrate_disk_and_power_off',
return_value='{}'
):
self._migrate_server(server)
def _resize(self, server):
flavors = self.api.get_flavors()
with mock.patch(
'nova.virt.libvirt.driver.LibvirtDriver' +
'.migrate_disk_and_power_off',
return_value='{}'
):
self._resize_server(server, flavors[1]['id'])
def _evacuate(self, server):
service_id = self.admin_api.get_services(
host="compute1", binary='nova-compute')[0]['id']
self.admin_api.put_service_force_down(service_id, True)
self._evacuate_server(server, expected_state='ACTIVE')
def test_cold_migrate_with_ip(self):
self._test_move_op(self._cold_migrate, migration_inbound_addr=None)
def test_cold_migrate_with_hostname(self):
self._test_move_op(self._cold_migrate, migration_inbound_addr="%s")
def test_resize_with_ip(self):
self._test_move_op(self._resize, migration_inbound_addr=None)
def test_resize_with_hostname(self):
self._test_move_op(self._resize, migration_inbound_addr="%s")
def test_evacuate_with_ip(self):
self._test_move_op(self._evacuate, migration_inbound_addr=None)
def test_evacuate_with_hostname(self):
self._test_move_op(self._evacuate, migration_inbound_addr="%s")

View File

@ -16626,6 +16626,29 @@ class LibvirtConnTestCase(test.NoDBTestCase,
ip = drvr.get_host_ip_addr()
self.assertEqual(ip, CONF.my_ip)
def test_get_host_ip_addr_defaults_to_my_ip(self):
CONF.set_default("my_ip", "10.0.0.3")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
ip = drvr.get_host_ip_addr()
self.assertEqual(ip, "10.0.0.3")
def test_get_host_ip_addr_override_via_migration_inbound_addr(self):
CONF.set_default("my_ip", "10.0.0.3")
CONF.set_default(
"migration_inbound_addr", "my-migration-hostname", group='libvirt')
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
ip = drvr.get_host_ip_addr()
self.assertEqual(ip, "my-migration-hostname")
def test_get_host_ip_addr_override_via_migration_inbound_addr_template(
self
):
CONF.set_default("my_ip", "10.0.0.3")
CONF.set_default("migration_inbound_addr", "%s", group='libvirt')
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
ip = drvr.get_host_ip_addr()
self.assertEqual(ip, "compute1")
@mock.patch.object(libvirt_driver.LOG, 'warning')
def test_check_my_ip(self, mock_log):
@ -19333,6 +19356,43 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_exists.assert_not_called()
mock_unlink.assert_not_called()
@mock.patch.object(os, 'unlink')
@mock.patch.object(os.path, 'exists')
@mock.patch('oslo_concurrency.processutils.execute')
def test_shared_storage_detection_same_host_migration_inbound_addr(
self, mock_exec, mock_exists, mock_unlink
):
CONF.set_default(
"migration_inbound_addr", "source-hostname", group="libvirt")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertTrue(drvr._is_path_shared_with('source-hostname', '/path'))
mock_exec.assert_not_called()
mock_exists.assert_not_called()
mock_unlink.assert_not_called()
@mock.patch.object(os.path, 'exists')
def test_shared_storage_detection_migration_inbound_addr(
self, mock_exists
):
CONF.set_default(
"migration_inbound_addr", "source-hostname", group="libvirt")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
mock_exists.return_value = False
with test.nested(
mock.patch.object(drvr._remotefs, 'create_file'),
mock.patch.object(drvr._remotefs, 'remove_file')
) as (mock_rem_fs_create, mock_rem_fs_remove):
self.assertFalse(
drvr._is_path_shared_with('dest-hostname', '/path'))
mock_rem_fs_create.assert_any_call('dest-hostname', mock.ANY)
create_args, create_kwargs = mock_rem_fs_create.call_args
self.assertTrue(create_args[1].startswith('/path'))
mock_rem_fs_remove.assert_called_with('dest-hostname', mock.ANY)
remove_args, remove_kwargs = mock_rem_fs_remove.call_args
self.assertTrue(remove_args[1].startswith('/path'))
def test_store_pid_remove_pid(self):
instance = objects.Instance(**self.test_instance)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)

View File

@ -4508,7 +4508,15 @@ class LibvirtDriver(driver.ComputeDriver):
return self._get_console_output_file(instance, console_path)
def get_host_ip_addr(self):
return CONF.my_ip
# NOTE(gibi): We should rename this as we might return a hostname
# instead of an IP address. But this is a virt driver interface
# method, so it probably does not worth the hashle. Only the
# resource_tracker use this today outside the virt driver to set up
# the Migration object.
addr = CONF.libvirt.migration_inbound_addr
if "%s" in addr:
addr = addr % self._host.get_hostname()
return addr
def get_vnc_console(self, context, instance):
def get_vnc_port_for_instance(instance_name):
@ -11455,9 +11463,10 @@ class LibvirtDriver(driver.ComputeDriver):
def _is_path_shared_with(self, dest, path):
# NOTE (rmk): There are two methods of determining whether we are
# on the same filesystem: the source and dest IP are the
# same, or we create a file on the dest system via SSH
# and check whether the source system can also see it.
# on the same filesystem: the source and dest migration
# address are the same, or we create a file on the dest
# system via SSH and check whether the source system can
# also see it.
shared_path = (dest == self.get_host_ip_addr())
if not shared_path:
tmp_file = uuidutils.generate_uuid(dashed=False) + '.tmp'

View File

@ -0,0 +1,11 @@
---
features:
- |
The new config option ``[libvirt]migration_inbound_addr`` is now used to
determine the address for incoming move operations (cold migrate, resize,
evacuate). This config is defaulted to [DEFAULT]my_ip to keep the
configuration backward compatible. However it allows an explicit hostname
or FQDN to be specified, or allows to specify '%s' that is then resolved to
the hostname of compute host.
Note that this config should only be changed from its default after every
compute is upgraded.