Skip to content

Commit 2b3fe47

Browse files
FionaLi-ocstephenfin
authored andcommitted
manager: Reduce unnecessary calls
The call to get_volume_connector() is unnecessary when all BDMs are not volume. It affects the performance of live migration or shutdown when instance hasn't attached volumes. Set the connector is none and call 'get_volume_connector' to assign a value to connector when it is first used. Change-Id: I46239b2a614858c435ad12cc69079a3e2b312b35
1 parent cd084ae commit 2b3fe47

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

nova/compute/manager.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2891,6 +2891,7 @@ def _shutdown_instance(self, context, instance,
28912891
self._try_deallocate_network(context, instance, requested_networks)
28922892

28932893
timer.restart()
2894+
connector = None
28942895
for bdm in vol_bdms:
28952896
try:
28962897
if bdm.attachment_id:
@@ -2899,7 +2900,8 @@ def _shutdown_instance(self, context, instance,
28992900
else:
29002901
# NOTE(vish): actual driver detach done in driver.destroy,
29012902
# so just tell cinder that we are done with it.
2902-
connector = self.driver.get_volume_connector(instance)
2903+
if connector is None:
2904+
connector = self.driver.get_volume_connector(instance)
29032905
self.volume_api.terminate_connection(context,
29042906
bdm.volume_id,
29052907
connector)
@@ -8138,7 +8140,7 @@ def pre_live_migration(self, context, instance, disk, migrate_data):
81388140
action=fields.NotificationAction.LIVE_MIGRATION_PRE,
81398141
phase=fields.NotificationPhase.START, bdms=bdms)
81408142

8141-
connector = self.driver.get_volume_connector(instance)
8143+
connector = None
81428144
try:
81438145
for bdm in bdms:
81448146
if bdm.is_volume and bdm.attachment_id is not None:
@@ -8152,6 +8154,8 @@ def pre_live_migration(self, context, instance, disk, migrate_data):
81528154
#
81538155
# Also note that attachment_update is not needed as we
81548156
# are providing the connector in the create call.
8157+
if connector is None:
8158+
connector = self.driver.get_volume_connector(instance)
81558159
attach_ref = self.volume_api.attachment_create(
81568160
context, bdm.volume_id, bdm.instance_uuid,
81578161
connector=connector, mountpoint=bdm.device_name)
@@ -8641,7 +8645,7 @@ def _post_live_migration_remove_source_vol_connections(
86418645
volumes with connection_info set for the source host
86428646
"""
86438647
# Detaching volumes.
8644-
connector = self.driver.get_volume_connector(instance)
8648+
connector = None
86458649
for bdm in source_bdms:
86468650
if bdm.is_volume:
86478651
# Detaching volumes is a call to an external API that can fail.
@@ -8661,6 +8665,9 @@ def _post_live_migration_remove_source_vol_connections(
86618665
# remove the volume connection without detaching from
86628666
# hypervisor because the instance is not running
86638667
# anymore on the current host
8668+
if connector is None:
8669+
connector = self.driver.get_volume_connector(
8670+
instance)
86648671
self.volume_api.terminate_connection(context,
86658672
bdm.volume_id,
86668673
connector)

nova/tests/unit/compute/test_compute.py

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11478,13 +11478,61 @@ def fake_volume_get(self, context, volume_id, microversion=None):
1147811478
mock_detach.assert_called_once_with(mock.ANY, uuids.volume,
1147911479
instance.uuid, None)
1148011480

11481+
@mock.patch.object(context.RequestContext, 'elevated')
11482+
@mock.patch.object(cinder.API, 'detach')
11483+
@mock.patch.object(cinder.API, 'terminate_connection')
11484+
@mock.patch.object(compute_manager.ComputeManager,
11485+
'_get_instance_block_device_info')
11486+
@mock.patch('nova.virt.fake.FakeDriver.get_volume_connector')
11487+
def test_shutdown_with_legacy_volume_detach(
11488+
self, mock_get_connector, mock_info, mock_terminate, mock_detach,
11489+
mock_elevated,
11490+
):
11491+
# test _shutdown_instance with legacy BDMs without a volume
11492+
# attachment ID
11493+
admin = context.get_admin_context()
11494+
mock_elevated.return_value = admin
11495+
instance = self._create_fake_instance_obj()
11496+
connector = 'fake-connector'
11497+
mock_get_connector.return_value = connector
11498+
11499+
vol_a_bdm = block_device_obj.BlockDeviceMapping(
11500+
instance_uuid=instance['uuid'],
11501+
source_type='volume', destination_type='volume',
11502+
delete_on_termination=False,
11503+
volume_id=uuids.volume_a_id,
11504+
attachment_id=None)
11505+
vol_b_bdm = block_device_obj.BlockDeviceMapping(
11506+
instance_uuid=instance['uuid'],
11507+
source_type='volume', destination_type='volume',
11508+
delete_on_termination=False,
11509+
volume_id=uuids.volume_b_id,
11510+
attachment_id=None)
11511+
bdms = [vol_a_bdm, vol_b_bdm]
11512+
11513+
self.compute._shutdown_instance(admin, instance, bdms)
11514+
11515+
# we should only got the connector once, regardless of the number of
11516+
# volumes
11517+
mock_get_connector.assert_called_once_with(instance)
11518+
# but we should have separate terminate and detach calls
11519+
mock_terminate.assert_has_calls([
11520+
mock.call(admin, uuids.volume_a_id, connector),
11521+
mock.call(admin, uuids.volume_b_id, connector),
11522+
])
11523+
mock_detach.assert_has_calls([
11524+
mock.call(admin, uuids.volume_a_id, instance.uuid),
11525+
mock.call(admin, uuids.volume_b_id, instance.uuid),
11526+
])
11527+
1148111528
@mock.patch.object(context.RequestContext, 'elevated')
1148211529
@mock.patch.object(cinder.API, 'attachment_delete')
1148311530
@mock.patch.object(compute_manager.ComputeManager,
1148411531
'_get_instance_block_device_info')
11485-
def test_shutdown_with_attachment_delete(self, mock_info,
11486-
mock_attach_delete,
11487-
mock_elevated):
11532+
@mock.patch('nova.virt.fake.FakeDriver.get_volume_connector')
11533+
def test_shutdown_with_attachment_delete(
11534+
self, mock_get_connector, mock_info, mock_attach_delete, mock_elevated,
11535+
):
1148811536
# test _shutdown_instance with volume bdm containing an
1148911537
# attachment id. This should use the v3 cinder api.
1149011538
admin = context.get_admin_context()
@@ -11504,14 +11552,18 @@ def test_shutdown_with_attachment_delete(self, mock_info,
1150411552
self.compute._shutdown_instance(admin, instance, bdms)
1150511553

1150611554
mock_attach_delete.assert_called_once_with(admin, attachment_id)
11555+
# we shouldn't try to get a connector for a cinder v3-style attachment
11556+
mock_get_connector.assert_not_called()
1150711557

1150811558
@mock.patch.object(compute_manager.LOG, 'debug')
1150911559
@mock.patch.object(cinder.API, 'attachment_delete')
1151011560
@mock.patch.object(compute_manager.ComputeManager,
1151111561
'_get_instance_block_device_info')
11512-
def test_shutdown_with_attachment_not_found(self, mock_info,
11513-
mock_attach_delete,
11514-
mock_debug_log):
11562+
@mock.patch('nova.virt.fake.FakeDriver.get_volume_connector')
11563+
def test_shutdown_with_attachment_not_found(
11564+
self, mock_get_connector, mock_info, mock_attach_delete,
11565+
mock_debug_log,
11566+
):
1151511567
# test _shutdown_instance with attachment_delete throwing
1151611568
# a VolumeAttachmentNotFound exception. This should not
1151711569
# cause _shutdown_instance to fail. Only a debug log
@@ -11537,6 +11589,8 @@ def test_shutdown_with_attachment_not_found(self, mock_info,
1153711589
# get last call to LOG.debug and verify correct exception is in there
1153811590
self.assertIsInstance(mock_debug_log.call_args[0][1],
1153911591
exception.VolumeAttachmentNotFound)
11592+
# we shouldn't try to get a connector for a cinder v3-style attachment
11593+
mock_get_connector.assert_not_called()
1154011594

1154111595
def test_terminate_with_volumes(self):
1154211596
# Make sure that volumes get detached during instance termination.

0 commit comments

Comments
 (0)