@@ -5790,7 +5790,10 @@ def test_rescue_bfv_with_required_trait(self, mock_get_bdms,
5790
5790
destination_type = 'volume' , volume_type = None ,
5791
5791
snapshot_id = None , volume_id = uuids .volume_id ,
5792
5792
volume_size = None )])
5793
- rescue_image_meta_obj = image_meta_obj .ImageMeta .from_dict ({})
5793
+ rescue_image_meta_obj = image_meta_obj .ImageMeta .from_dict ({
5794
+ 'properties' : {'hw_rescue_device' : 'disk' ,
5795
+ 'hw_rescue_bus' : 'scsi' }
5796
+ })
5794
5797
5795
5798
with test .nested (
5796
5799
mock .patch .object (self .compute_api .placementclient ,
@@ -5842,6 +5845,7 @@ def test_rescue_bfv_with_required_trait(self, mock_get_bdms,
5842
5845
# Assert that the instance task state as set in the compute API
5843
5846
self .assertEqual (task_states .RESCUING , instance .task_state )
5844
5847
5848
+ @mock .patch ('nova.objects.instance.Instance.image_meta' )
5845
5849
@mock .patch ('nova.objects.compute_node.ComputeNode'
5846
5850
'.get_by_host_and_nodename' )
5847
5851
@mock .patch ('nova.compute.utils.is_volume_backed_instance' ,
@@ -5850,14 +5854,21 @@ def test_rescue_bfv_with_required_trait(self, mock_get_bdms,
5850
5854
'.get_by_instance_uuid' )
5851
5855
def test_rescue_bfv_without_required_trait (self , mock_get_bdms ,
5852
5856
mock_is_volume_backed ,
5853
- mock_get_cn ):
5857
+ mock_get_cn ,
5858
+ mock_image_meta ):
5854
5859
instance = self ._create_instance_obj ()
5855
5860
bdms = objects .BlockDeviceMappingList (objects = [
5856
5861
objects .BlockDeviceMapping (
5857
5862
boot_index = 0 , image_id = uuids .image_id , source_type = 'image' ,
5858
5863
destination_type = 'volume' , volume_type = None ,
5859
5864
snapshot_id = None , volume_id = uuids .volume_id ,
5860
5865
volume_size = None )])
5866
+
5867
+ instance .image_meta = image_meta_obj .ImageMeta .from_dict ({
5868
+ 'properties' : {'hw_rescue_device' : 'disk' ,
5869
+ 'hw_rescue_bus' : 'scsi' }
5870
+ })
5871
+
5861
5872
with test .nested (
5862
5873
mock .patch .object (self .compute_api .placementclient ,
5863
5874
'get_provider_traits' ),
@@ -5895,6 +5906,124 @@ def test_rescue_bfv_without_required_trait(self, mock_get_bdms,
5895
5906
mock_get_traits .assert_called_once_with (
5896
5907
self .context , uuids .cn )
5897
5908
5909
+ @mock .patch ('nova.objects.image_meta.ImageMeta.from_image_ref' )
5910
+ @mock .patch ('nova.objects.compute_node.ComputeNode'
5911
+ '.get_by_host_and_nodename' )
5912
+ @mock .patch ('nova.compute.utils.is_volume_backed_instance' ,
5913
+ return_value = True )
5914
+ @mock .patch ('nova.objects.block_device.BlockDeviceMappingList'
5915
+ '.get_by_instance_uuid' )
5916
+ def test_rescue_bfv_with_required_image_properties (
5917
+ self , mock_get_bdms , mock_is_volume_backed , mock_get_cn ,
5918
+ mock_image_meta_obj_from_ref ):
5919
+ instance = self ._create_instance_obj ()
5920
+ bdms = objects .BlockDeviceMappingList (objects = [
5921
+ objects .BlockDeviceMapping (
5922
+ boot_index = 0 , image_id = uuids .image_id , source_type = 'image' ,
5923
+ destination_type = 'volume' , volume_type = None ,
5924
+ snapshot_id = None , volume_id = uuids .volume_id ,
5925
+ volume_size = None )])
5926
+ rescue_image_meta_obj = image_meta_obj .ImageMeta .from_dict ({
5927
+ 'properties' : {'hw_rescue_device' : 'disk' ,
5928
+ 'hw_rescue_bus' : 'scsi' }
5929
+ })
5930
+
5931
+ with test .nested (
5932
+ mock .patch .object (self .compute_api .placementclient ,
5933
+ 'get_provider_traits' ),
5934
+ mock .patch .object (self .compute_api .volume_api , 'get' ),
5935
+ mock .patch .object (self .compute_api .volume_api , 'check_attached' ),
5936
+ mock .patch .object (instance , 'save' ),
5937
+ mock .patch .object (self .compute_api , '_record_action_start' ),
5938
+ mock .patch .object (self .compute_api .compute_rpcapi ,
5939
+ 'rescue_instance' )
5940
+ ) as (
5941
+ mock_get_traits , mock_get_volume , mock_check_attached ,
5942
+ mock_instance_save , mock_record_start , mock_rpcapi_rescue
5943
+ ):
5944
+ # Mock out the returned compute node, image_meta, bdms and volume
5945
+ mock_image_meta_obj_from_ref .return_value = rescue_image_meta_obj
5946
+ mock_get_bdms .return_value = bdms
5947
+ mock_get_volume .return_value = mock .sentinel .volume
5948
+ mock_get_cn .return_value = mock .Mock (uuid = uuids .cn )
5949
+
5950
+ # Ensure the required trait is returned, allowing BFV rescue
5951
+ mock_trait_info = mock .Mock (traits = [ot .COMPUTE_RESCUE_BFV ])
5952
+ mock_get_traits .return_value = mock_trait_info
5953
+
5954
+ # Try to rescue the instance
5955
+ self .compute_api .rescue (self .context , instance ,
5956
+ rescue_image_ref = uuids .rescue_image_id ,
5957
+ allow_bfv_rescue = True )
5958
+
5959
+ # Assert all of the calls made in the compute API
5960
+ mock_get_bdms .assert_called_once_with (self .context , instance .uuid )
5961
+ mock_get_volume .assert_called_once_with (
5962
+ self .context , uuids .volume_id )
5963
+ mock_check_attached .assert_called_once_with (
5964
+ self .context , mock .sentinel .volume )
5965
+ mock_is_volume_backed .assert_called_once_with (
5966
+ self .context , instance , bdms )
5967
+ mock_get_cn .assert_called_once_with (
5968
+ self .context , instance .host , instance .node )
5969
+ mock_get_traits .assert_called_once_with (self .context , uuids .cn )
5970
+ mock_instance_save .assert_called_once_with (
5971
+ expected_task_state = [None ])
5972
+ mock_record_start .assert_called_once_with (
5973
+ self .context , instance , instance_actions .RESCUE )
5974
+ mock_rpcapi_rescue .assert_called_once_with (
5975
+ self .context , instance = instance , rescue_password = None ,
5976
+ rescue_image_ref = uuids .rescue_image_id , clean_shutdown = True )
5977
+
5978
+ # Assert that the instance task state as set in the compute API
5979
+ self .assertEqual (task_states .RESCUING , instance .task_state )
5980
+
5981
+ @mock .patch ('nova.objects.image_meta.ImageMeta.from_image_ref' )
5982
+ @mock .patch ('nova.compute.utils.is_volume_backed_instance' ,
5983
+ return_value = True )
5984
+ @mock .patch ('nova.objects.block_device.BlockDeviceMappingList'
5985
+ '.get_by_instance_uuid' )
5986
+ def test_rescue_bfv_without_required_image_properties (
5987
+ self , mock_get_bdms , mock_is_volume_backed ,
5988
+ mock_image_meta_obj_from_ref ):
5989
+ instance = self ._create_instance_obj ()
5990
+ bdms = objects .BlockDeviceMappingList (objects = [
5991
+ objects .BlockDeviceMapping (
5992
+ boot_index = 0 , image_id = uuids .image_id , source_type = 'image' ,
5993
+ destination_type = 'volume' , volume_type = None ,
5994
+ snapshot_id = None , volume_id = uuids .volume_id ,
5995
+ volume_size = None )])
5996
+ rescue_image_meta_obj = image_meta_obj .ImageMeta .from_dict ({
5997
+ 'properties' : {}
5998
+ })
5999
+
6000
+ with test .nested (
6001
+ mock .patch .object (self .compute_api .volume_api , 'get' ),
6002
+ mock .patch .object (self .compute_api .volume_api , 'check_attached' ),
6003
+ ) as (
6004
+ mock_get_volume , mock_check_attached
6005
+ ):
6006
+ # Mock out the returned bdms, volume and image_meta
6007
+ mock_get_bdms .return_value = bdms
6008
+ mock_get_volume .return_value = mock .sentinel .volume
6009
+ mock_image_meta_obj_from_ref .return_value = rescue_image_meta_obj
6010
+
6011
+ # Assert that any attempt to rescue a bfv instance on a compute
6012
+ # node that does not report the COMPUTE_RESCUE_BFV trait fails and
6013
+ # raises InstanceNotRescuable
6014
+ self .assertRaises (exception .InstanceNotRescuable ,
6015
+ self .compute_api .rescue , self .context , instance ,
6016
+ rescue_image_ref = None , allow_bfv_rescue = True )
6017
+
6018
+ # Assert the calls made in the compute API prior to the failure
6019
+ mock_get_bdms .assert_called_once_with (self .context , instance .uuid )
6020
+ mock_get_volume .assert_called_once_with (
6021
+ self .context , uuids .volume_id )
6022
+ mock_check_attached .assert_called_once_with (
6023
+ self .context , mock .sentinel .volume )
6024
+ mock_is_volume_backed .assert_called_once_with (
6025
+ self .context , instance , bdms )
6026
+
5898
6027
@mock .patch ('nova.compute.utils.is_volume_backed_instance' ,
5899
6028
return_value = True )
5900
6029
@mock .patch ('nova.objects.block_device.BlockDeviceMappingList'
0 commit comments