[libvirt]Add migration_inbound_addr
For live migration the libvirt driver already supports generating the migration URL based on the compute host hostname if so configured. However for the non live move operations the driver always used the IP address of the compute host based on [DEFAULT]my_ip. Some deployments rely on DNS to abstract the IP address management. In these environments it is beneficial if nova allows connection between compute hosts based on the hostname (or FQDN) of the host instead of trying to configure [DEFAUL]my_ip to an IP address. This patch introduces a new config option [libvirt]migration_inbound_addr that is 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. blueprint: libvirt-migrate-with-hostname-instead-of-ip Change-Id: I6a80b5620f32770a04c751143c4ad07882e9f812
This commit is contained in:
parent
96d7c42e27
commit
6bca37e904
@ -916,6 +916,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``
|
||||
|
||||
"""),
|
||||
]
|
||||
|
||||
|
91
nova/tests/functional/libvirt/test_migration_addr.py
Normal file
91
nova/tests/functional/libvirt/test_migration_addr.py
Normal 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")
|
@ -16624,6 +16624,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):
|
||||
|
||||
@ -19331,6 +19354,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)
|
||||
|
@ -4512,7 +4512,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):
|
||||
@ -11461,9 +11469,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'
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user