Merge "[libvirt]Add migration_inbound_addr"
This commit is contained in:
commit
33ab9c5d0e
@ -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``
|
||||
|
||||
"""),
|
||||
]
|
||||
|
||||
|
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")
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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…
x
Reference in New Issue
Block a user