Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAP: Use is_instance_storage_share instead of ssh #513

Open
wants to merge 1 commit into
base: stable/xena-m3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion nova/compute/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,10 @@ def update_compute_provider_status(self, context, rp_uuid, enabled):
self.reportclient.set_traits_for_provider(
context, rp_uuid, new_traits, generation=trait_info.generation)

def is_instance_storage_shared(self, context, instance, host=None):
return self._compute._is_instance_storage_shared(context, instance,
host=host)


class ComputeManager(manager.Manager):
"""Manages the running instances from creation to destruction."""
Expand Down Expand Up @@ -5632,7 +5636,7 @@ def _resize_instance(
context, instance, migration.dest_host,
flavor, network_info,
block_device_info,
timeout, retry_interval)
timeout, retry_interval, migration.dest_compute)

self._terminate_volume_connections(context, instance, bdms)

Expand Down
23 changes: 23 additions & 0 deletions nova/tests/unit/compute/test_virtapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ def test_update_compute_provider_status(self):
nova_context.get_admin_context(), uuids.rp_uuid,
enabled=False)

def test_is_instance_storage_shared(self):
self.assertExpected('is_instance_storage_shared',
nova_context.get_admin_context(),
mock.sentinel.instance,
mock.sentinel.host)


class FakeVirtAPITest(VirtAPIBaseTest):

Expand All @@ -74,6 +80,8 @@ def assertExpected(self, method, *args, **kwargs):
self.virtapi.exit_wait_early(*args, **kwargs)
elif method == 'update_compute_provider_status':
self.virtapi.update_compute_provider_status(*args, **kwargs)
elif method == 'is_instance_storage_shared':
self.virtapi.is_instance_storage_shared(*args, **kwargs)
else:
self.fail("Unhandled FakeVirtAPI method: %s" % method)

Expand Down Expand Up @@ -116,6 +124,9 @@ def _prepare_for_instance_event(self, instance, name, tag):
self._events.append(m)
return m

def _is_instance_storage_shared(self, context, instance, host):
return


class ComputeVirtAPITest(VirtAPIBaseTest):

Expand Down Expand Up @@ -230,3 +241,15 @@ def test_update_compute_provider_status(self):
self.virtapi.update_compute_provider_status(
ctxt, uuids.rp_uuid, enabled=True)
self.assertEqual(set(), self.compute.provider_traits[uuids.rp_uuid])

def test_is_instance_storage_shared(self):
ctxt = nova_context.get_admin_context()

with mock.patch.object(self.compute,
'_is_instance_storage_shared') as mock_shared:
self.virtapi.is_instance_storage_shared(ctxt,
mock.sentinel.instance,
host=mock.sentinel.host)
mock_shared.assert_called_once_with(ctxt,
mock.sentinel.instance,
host=mock.sentinel.host)
59 changes: 23 additions & 36 deletions nova/tests/unit/virt/libvirt/test_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18512,48 +18512,33 @@ def test_set_cache_mode_invalid_object(self):
drvr._set_cache_mode(fake_conf)
self.assertEqual(fake_conf.driver_cache, 'fake')

@mock.patch('os.unlink')
@mock.patch.object(os.path, 'exists')
@mock.patch.object(fake.FakeVirtAPI, 'is_instance_storage_shared')
def _test_shared_storage_detection(self, is_same,
mock_exists, mock_unlink):
mock_is_instance_storage_shared):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr.get_host_ip_addr = mock.MagicMock(return_value='bar')
mock_exists.return_value = is_same
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):
result = drvr._is_path_shared_with('host', '/path')
mock_rem_fs_create.assert_any_call('host', mock.ANY)
create_args, create_kwargs = mock_rem_fs_create.call_args
self.assertTrue(create_args[1].startswith('/path'))
if is_same:
mock_unlink.assert_called_once_with(mock.ANY)
else:
mock_rem_fs_remove.assert_called_with('host', mock.ANY)
remove_args, remove_kwargs = mock_rem_fs_remove.call_args
self.assertTrue(remove_args[1].startswith('/path'))
return result
mock_is_instance_storage_shared.return_value = is_same
return drvr._is_instance_storage_shared(None,
mock.sentinel.instance,
'host',
mock.sentinel.dest_host)

def test_shared_storage_detection_same_host(self):
self.assertTrue(self._test_shared_storage_detection(True))

def test_shared_storage_detection_different_host(self):
self.assertFalse(self._test_shared_storage_detection(False))

@mock.patch.object(os, 'unlink')
@mock.patch.object(os.path, 'exists')
@mock.patch('oslo_concurrency.processutils.execute')
@mock.patch.object(fake.FakeVirtAPI, 'is_instance_storage_shared')
@mock.patch.object(libvirt_driver.LibvirtDriver, 'get_host_ip_addr',
return_value='foo')
def test_shared_storage_detection_easy(self, mock_get, mock_exec,
mock_exists, mock_unlink):
def test_shared_storage_detection_easy(self, mock_get,
mock_is_instance_storage_shared):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertTrue(drvr._is_path_shared_with('foo', '/path'))
self.assertTrue(drvr._is_instance_storage_shared(
None, mock.sentinel.instance, 'foo', mock.sentinel.dest_host))
mock_get.assert_called_once_with()
mock_exec.assert_not_called()
mock_exists.assert_not_called()
mock_unlink.assert_not_called()
mock_is_instance_storage_shared.assert_not_called()

def test_store_pid_remove_pid(self):
instance = objects.Instance(**self.test_instance)
Expand Down Expand Up @@ -21585,7 +21570,7 @@ def _create_instance(self, params=None):
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.get_host_ip_addr',
return_value='10.0.0.1')
@mock.patch(('nova.virt.libvirt.driver.LibvirtDriver.'
'_is_path_shared_with'), return_value=False)
'_is_instance_storage_shared'), return_value=False)
@mock.patch('os.rename')
@mock.patch('os.path.exists', return_value=True)
@mock.patch('oslo_concurrency.processutils.execute',
Expand Down Expand Up @@ -21616,7 +21601,7 @@ def test_migrate_disk_and_power_off_exception(
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.get_host_ip_addr',
return_value='10.0.0.1')
@mock.patch(('nova.virt.libvirt.driver.LibvirtDriver.'
'_is_path_shared_with'), return_value=False)
'_is_instance_storage_shared'), return_value=False)
@mock.patch('os.rename')
@mock.patch('os.path.exists', return_value=True)
@mock.patch('oslo_concurrency.processutils.execute')
Expand Down Expand Up @@ -21656,6 +21641,7 @@ def _test_migrate_disk_and_power_off(
mock_vtpm.assert_called_with(
instance.uuid, mock.ANY, mock.ANY, '10.0.0.1', mock.ANY, mock.ANY)
mock_unplug_vifs.assert_called_once()
return instance

def test_migrate_disk_and_power_off(self):
flavor = {'root_gb': 10, 'ephemeral_gb': 20}
Expand Down Expand Up @@ -21768,14 +21754,14 @@ def _test_migrate_disk_and_power_off_resize_check(self, expected_exc):
"""Test for nova.virt.libvirt.libvirt_driver.LibvirtConnection
.migrate_disk_and_power_off.
"""
instance = self._create_instance()
self.instance = self._create_instance()
flavor = {'root_gb': 10, 'ephemeral_gb': 20}
flavor_obj = objects.Flavor(**flavor)

# Migration is not implemented for LVM backed instances
self.assertRaises(expected_exc,
self.drvr.migrate_disk_and_power_off,
None, instance, '10.0.0.1', flavor_obj, None)
None, self.instance, '10.0.0.1', flavor_obj, None)

@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.unplug_vifs',
new=mock.Mock())
Expand All @@ -21786,7 +21772,7 @@ def _test_migrate_disk_and_power_off_resize_check(self, expected_exc):
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
'._get_instance_disk_info')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
'._is_path_shared_with')
'._is_instance_storage_shared')
def _test_migrate_disk_and_power_off_backing_file(self,
shared_storage,
mock_is_shared_storage,
Expand Down Expand Up @@ -21839,7 +21825,7 @@ def test_migrate_disk_and_power_off_lvm(self):
self._test_migrate_disk_and_power_off_resize_check(expected_exc)

@mock.patch.object(libvirt_driver.LibvirtDriver,
'_is_path_shared_with', return_value=False)
'_is_instance_storage_shared', return_value=False)
def test_migrate_disk_and_power_off_resize_cannot_ssh(self,
mock_is_shared):
def fake_execute(*args, **kwargs):
Expand All @@ -21848,7 +21834,8 @@ def fake_execute(*args, **kwargs):

expected_exc = exception.InstanceFaultRollback
self._test_migrate_disk_and_power_off_resize_check(expected_exc)
mock_is_shared.assert_called_once_with('10.0.0.1', test.MatchType(str))
mock_is_shared.assert_called_once_with(None, self.instance, '10.0.0.1',
None)

@mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
'._get_instance_disk_info')
Expand Down Expand Up @@ -21968,7 +21955,7 @@ def test_migrate_disk_and_power_off_resize_error_eph(self, mock_get,
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._destroy')
@mock.patch('nova.virt.libvirt.utils.get_instance_path')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
'._is_path_shared_with')
'._is_instance_storage_shared')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
'._get_instance_disk_info')
def test_migrate_disk_and_power_off_resize_copy_disk_info(
Expand Down
5 changes: 4 additions & 1 deletion nova/virt/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,8 @@ def detach_interface(self, context, instance, vif):
def migrate_disk_and_power_off(self, context, instance, dest,
flavor, network_info,
block_device_info=None,
timeout=0, retry_interval=0):
timeout=0, retry_interval=0,
dest_compute=None):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are changing this, might have been beneficial to bring in the whole migration object here.

Currently the VMware driver does an extra db call to fetch the migration by instance_uuid, which can be removed if we had the migration object already here.

What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I assumed upstream chose explicitly not to expose the migration object, but if we need it in vmware-driver anyway...
I'll have a quick look how much I'd need to change for that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #517 for that... but I am not sure, if the additional changes are paying off.

"""Transfers the disk of a running instance in multiple phases, turning
off the instance before the end.
Expand All @@ -709,6 +710,8 @@ def migrate_disk_and_power_off(self, context, instance, dest,
The time in seconds to wait for the guest OS to shutdown.
:param int retry_interval:
How often to signal guest while waiting for it to shutdown.
:param str dest_compute:
The name of the destination compute-host
:return: A list of disk information dicts in JSON format.
:rtype: str
Expand Down
6 changes: 5 additions & 1 deletion nova/virt/fake.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,8 @@ def poll_rebooting_instances(self, timeout, instances):
def migrate_disk_and_power_off(self, context, instance, dest,
flavor, network_info,
block_device_info=None,
timeout=0, retry_interval=0):
timeout=0, retry_interval=0,
dest_compute=None):
pass

def finish_revert_migration(self, context, instance, network_info,
Expand Down Expand Up @@ -670,6 +671,9 @@ def exit_wait_early(self, events):
def update_compute_provider_status(self, context, rp_uuid, enabled):
pass

def is_instance_storage_shared(self, context, instance, host=None):
pass


class FakeComputeVirtAPI(FakeVirtAPI):
def __init__(self, compute):
Expand Down
3 changes: 2 additions & 1 deletion nova/virt/hyperv/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@ def unplug_vifs(self, instance, network_info):
def migrate_disk_and_power_off(self, context, instance, dest,
flavor, network_info,
block_device_info=None,
timeout=0, retry_interval=0):
timeout=0, retry_interval=0,
dest_compute=None):
return self._migrationops.migrate_disk_and_power_off(context,
instance, dest,
flavor,
Expand Down
34 changes: 15 additions & 19 deletions nova/virt/libvirt/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -11036,31 +11036,24 @@ def _get_disk_size_reserved_for_image_cache(self):
return compute_utils.convert_mb_to_ceil_gb(
self.image_cache_manager.get_disk_usage() / 1024.0 / 1024.0)

def _is_path_shared_with(self, dest, path):
def _is_instance_storage_shared(self, context, instance, dest,
dest_compute):
# 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.
shared_path = (dest == self.get_host_ip_addr())
if not shared_path:
tmp_file = uuidutils.generate_uuid(dashed=False) + '.tmp'
tmp_path = os.path.join(path, tmp_file)
# same...
if dest == self.get_host_ip_addr():
return True

try:
self._remotefs.create_file(dest, tmp_path)
if os.path.exists(tmp_path):
shared_path = True
os.unlink(tmp_path)
else:
self._remotefs.remove_file(dest, tmp_path)
except Exception:
pass
return shared_path
# NOTE (fwiesel): Or we rely on the drivers api pair
# check_instance_shared_storage_local / check_instance_shared_storage
return self.virtapi.is_instance_storage_shared(context, instance,
dest_compute)

def migrate_disk_and_power_off(self, context, instance, dest,
flavor, network_info,
block_device_info=None,
timeout=0, retry_interval=0):
timeout=0, retry_interval=0,
dest_compute=None):
LOG.debug("Starting migrate_disk_and_power_off",
instance=instance)

Expand Down Expand Up @@ -11095,7 +11088,10 @@ def migrate_disk_and_power_off(self, context, instance, dest,
# shared storage for instance dir (eg. NFS).
inst_base = libvirt_utils.get_instance_path(instance)
inst_base_resize = inst_base + "_resize"
shared_instance_path = self._is_path_shared_with(dest, inst_base)
shared_instance_path = self._is_instance_storage_shared(context,
instance,
dest,
dest_compute)

# try to create the directory on the remote compute node
# if this fails we pass the exception up the stack so we can catch
Expand Down
3 changes: 3 additions & 0 deletions nova/virt/virtapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ def update_compute_provider_status(self, context, rp_uuid, enabled):
the trait would be added.
"""
raise NotImplementedError()

def is_instance_storage_shared(self, context, instance, host=None):
raise NotImplementedError()
3 changes: 2 additions & 1 deletion nova/virt/vmwareapi/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ def list_instances(self):
def migrate_disk_and_power_off(self, context, instance, dest,
flavor, network_info,
block_device_info=None,
timeout=0, retry_interval=0):
timeout=0, retry_interval=0,
dest_compute=None):
"""Transfers the disk of a running instance in multiple phases, turning
off the instance before the end.
"""
Expand Down