1# Copyright (c) 2014 Andrew Kerr. All rights reserved. 2# Copyright (c) 2015 Tom Barron. All rights reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15""" 16Mock unit tests for the NetApp cmode nfs storage driver 17""" 18 19import hashlib 20import uuid 21 22import ddt 23import mock 24from os_brick.remotefs import remotefs as remotefs_brick 25from oslo_utils import units 26 27from cinder import exception 28from cinder.image import image_utils 29from cinder.objects import fields 30from cinder import test 31from cinder.tests.unit.volume.drivers.netapp.dataontap import fakes as fake 32from cinder.tests.unit.volume.drivers.netapp.dataontap.utils import fakes as \ 33 fake_ssc 34from cinder.tests.unit.volume.drivers.netapp import fakes as na_fakes 35from cinder import utils 36from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api 37from cinder.volume.drivers.netapp.dataontap.client import client_cmode 38from cinder.volume.drivers.netapp.dataontap import nfs_base 39from cinder.volume.drivers.netapp.dataontap import nfs_cmode 40from cinder.volume.drivers.netapp.dataontap.performance import perf_cmode 41from cinder.volume.drivers.netapp.dataontap.utils import capabilities 42from cinder.volume.drivers.netapp.dataontap.utils import data_motion 43from cinder.volume.drivers.netapp.dataontap.utils import loopingcalls 44from cinder.volume.drivers.netapp.dataontap.utils import utils as dot_utils 45from cinder.volume.drivers.netapp import utils as na_utils 46from cinder.volume.drivers import nfs 47from cinder.volume import utils as volume_utils 48 49 50@ddt.ddt 51class NetAppCmodeNfsDriverTestCase(test.TestCase): 52 def setUp(self): 53 super(NetAppCmodeNfsDriverTestCase, self).setUp() 54 55 kwargs = { 56 'configuration': self.get_config_cmode(), 57 'host': 'openstack@nfscmode', 58 } 59 60 with mock.patch.object(utils, 'get_root_helper', 61 return_value=mock.Mock()): 62 with mock.patch.object(remotefs_brick, 'RemoteFsClient', 63 return_value=mock.Mock()): 64 self.driver = nfs_cmode.NetAppCmodeNfsDriver(**kwargs) 65 self.driver._mounted_shares = [fake.NFS_SHARE] 66 self.driver.ssc_vols = True 67 self.driver.vserver = fake.VSERVER_NAME 68 self.driver.ssc_enabled = True 69 self.driver.perf_library = mock.Mock() 70 self.driver.ssc_library = mock.Mock() 71 self.driver.zapi_client = mock.Mock() 72 self.driver.using_cluster_credentials = True 73 74 def get_config_cmode(self): 75 config = na_fakes.create_configuration_cmode() 76 config.netapp_storage_protocol = 'nfs' 77 config.netapp_login = 'admin' 78 config.netapp_password = 'pass' 79 config.netapp_server_hostname = '127.0.0.1' 80 config.netapp_transport_type = 'http' 81 config.netapp_server_port = '80' 82 config.netapp_vserver = fake.VSERVER_NAME 83 config.netapp_copyoffload_tool_path = 'copyoffload_tool_path' 84 config.netapp_api_trace_pattern = 'fake_regex' 85 return config 86 87 @ddt.data({'active_backend_id': None, 'targets': ['dev1', 'dev2']}, 88 {'active_backend_id': None, 'targets': []}, 89 {'active_backend_id': 'dev1', 'targets': []}, 90 {'active_backend_id': 'dev1', 'targets': ['dev1', 'dev2']}) 91 @ddt.unpack 92 def test_init_driver_for_replication(self, active_backend_id, 93 targets): 94 kwargs = { 95 'configuration': self.get_config_cmode(), 96 'host': 'openstack@nfscmode', 97 'active_backend_id': active_backend_id, 98 } 99 self.mock_object(data_motion.DataMotionMixin, 100 'get_replication_backend_names', 101 return_value=targets) 102 with mock.patch.object(utils, 'get_root_helper', 103 return_value=mock.Mock()): 104 with mock.patch.object(remotefs_brick, 'RemoteFsClient', 105 return_value=mock.Mock()): 106 nfs_driver = nfs_cmode.NetAppCmodeNfsDriver(**kwargs) 107 108 self.assertEqual(active_backend_id, 109 nfs_driver.failed_over_backend_name) 110 self.assertEqual(active_backend_id is not None, 111 nfs_driver.failed_over) 112 self.assertEqual(len(targets) > 0, 113 nfs_driver.replication_enabled) 114 115 @mock.patch.object(perf_cmode, 'PerformanceCmodeLibrary', mock.Mock()) 116 @mock.patch.object(client_cmode, 'Client', mock.Mock()) 117 @mock.patch.object(capabilities.CapabilitiesLibrary, 118 'cluster_user_supported') 119 @mock.patch.object(capabilities.CapabilitiesLibrary, 120 'check_api_permissions') 121 @mock.patch.object(nfs.NfsDriver, 'do_setup') 122 @mock.patch.object(na_utils, 'check_flags') 123 def test_do_setup(self, mock_check_flags, mock_super_do_setup, 124 mock_check_api_permissions, mock_cluster_user_supported): 125 self.mock_object( 126 dot_utils, 'get_backend_configuration', 127 return_value=self.get_config_cmode()) 128 129 self.driver.do_setup(mock.Mock()) 130 131 self.assertTrue(mock_check_flags.called) 132 self.assertTrue(mock_super_do_setup.called) 133 mock_check_api_permissions.assert_called_once_with() 134 mock_cluster_user_supported.assert_called_once_with() 135 136 def test__update_volume_stats(self): 137 mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug') 138 self.mock_object(self.driver, 'get_filter_function') 139 self.mock_object(self.driver, 'get_goodness_function') 140 self.mock_object(self.driver, '_spawn_clean_cache_job') 141 self.driver.zapi_client = mock.Mock() 142 self.mock_object(self.driver, '_get_pool_stats', return_value={}) 143 expected_stats = { 144 'driver_version': self.driver.VERSION, 145 'pools': {}, 146 'sparse_copy_volume': True, 147 'replication_enabled': False, 148 'storage_protocol': 'nfs', 149 'vendor_name': 'NetApp', 150 'volume_backend_name': 'NetApp_NFS_Cluster_direct', 151 } 152 153 retval = self.driver._update_volume_stats() 154 155 self.assertIsNone(retval) 156 self.assertTrue(self.driver._spawn_clean_cache_job.called) 157 self.assertEqual(1, mock_debug_log.call_count) 158 self.assertEqual(expected_stats, self.driver._stats) 159 160 @ddt.data({'replication_backends': [], 'cluster_credentials': False}, 161 {'replication_backends': ['target_1', 'target_2'], 162 'cluster_credentials': True}) 163 @ddt.unpack 164 def test_get_pool_stats(self, replication_backends, cluster_credentials): 165 self.driver.using_cluster_credentials = cluster_credentials 166 self.driver.zapi_client = mock.Mock() 167 ssc = { 168 'vola': { 169 'pool_name': '10.10.10.10:/vola', 170 'thick_provisioning_support': True, 171 'thin_provisioning_support': False, 172 'netapp_thin_provisioned': 'false', 173 'netapp_compression': 'false', 174 'netapp_mirrored': 'false', 175 'netapp_dedup': 'true', 176 'netapp_aggregate': 'aggr1', 177 'netapp_raid_type': 'raid_dp', 178 'netapp_disk_type': 'SSD', 179 'consistent_group_snapshot_enabled': True, 180 }, 181 } 182 mock_get_ssc = self.mock_object(self.driver.ssc_library, 183 'get_ssc', 184 return_value=ssc) 185 mock_get_aggrs = self.mock_object(self.driver.ssc_library, 186 'get_ssc_aggregates', 187 return_value=['aggr1']) 188 189 self.mock_object(self.driver, 'get_replication_backend_names', 190 return_value=replication_backends) 191 192 total_capacity_gb = na_utils.round_down( 193 fake.TOTAL_BYTES // units.Gi, '0.01') 194 free_capacity_gb = na_utils.round_down( 195 fake.AVAILABLE_BYTES // units.Gi, '0.01') 196 capacity = { 197 'reserved_percentage': fake.RESERVED_PERCENTAGE, 198 'max_over_subscription_ratio': fake.MAX_OVER_SUBSCRIPTION_RATIO, 199 'total_capacity_gb': total_capacity_gb, 200 'free_capacity_gb': free_capacity_gb, 201 } 202 self.mock_object(self.driver, 203 '_get_share_capacity_info', 204 return_value=capacity) 205 self.mock_object(self.driver.zapi_client, 206 'get_flexvol_dedupe_used_percent', 207 return_value=55.0) 208 209 aggr_capacities = { 210 'aggr1': { 211 'percent-used': 45, 212 'size-available': 59055800320.0, 213 'size-total': 107374182400.0, 214 }, 215 } 216 mock_get_aggr_capacities = self.mock_object( 217 self.driver.zapi_client, 'get_aggregate_capacities', 218 return_value=aggr_capacities) 219 220 self.driver.perf_library.get_node_utilization_for_pool = ( 221 mock.Mock(return_value=30.0)) 222 223 result = self.driver._get_pool_stats(filter_function='filter', 224 goodness_function='goodness') 225 226 expected = [{ 227 'pool_name': '10.10.10.10:/vola', 228 'reserved_percentage': fake.RESERVED_PERCENTAGE, 229 'max_over_subscription_ratio': fake.MAX_OVER_SUBSCRIPTION_RATIO, 230 'multiattach': False, 231 'total_capacity_gb': total_capacity_gb, 232 'free_capacity_gb': free_capacity_gb, 233 'netapp_dedupe_used_percent': 55.0, 234 'netapp_aggregate_used_percent': 45, 235 'utilization': 30.0, 236 'filter_function': 'filter', 237 'goodness_function': 'goodness', 238 'thick_provisioning_support': True, 239 'thin_provisioning_support': False, 240 'netapp_thin_provisioned': 'false', 241 'netapp_compression': 'false', 242 'netapp_mirrored': 'false', 243 'netapp_dedup': 'true', 244 'netapp_aggregate': 'aggr1', 245 'netapp_raid_type': 'raid_dp', 246 'netapp_disk_type': 'SSD', 247 'consistencygroup_support': True, 248 'consistent_group_snapshot_enabled': True, 249 'replication_enabled': False, 250 }] 251 252 expected[0].update({'QoS_support': cluster_credentials}) 253 if not cluster_credentials: 254 expected[0].update({ 255 'netapp_aggregate_used_percent': 0, 256 'netapp_dedupe_used_percent': 0 257 }) 258 259 if replication_backends: 260 expected[0].update({ 261 'replication_enabled': True, 262 'replication_count': len(replication_backends), 263 'replication_targets': replication_backends, 264 'replication_type': 'async', 265 }) 266 267 self.assertEqual(expected, result) 268 mock_get_ssc.assert_called_once_with() 269 if cluster_credentials: 270 mock_get_aggrs.assert_called_once_with() 271 mock_get_aggr_capacities.assert_called_once_with(['aggr1']) 272 273 @ddt.data({}, None) 274 def test_get_pool_stats_no_ssc_vols(self, ssc): 275 276 mock_get_ssc = self.mock_object(self.driver.ssc_library, 277 'get_ssc', 278 return_value=ssc) 279 280 pools = self.driver._get_pool_stats() 281 282 self.assertListEqual([], pools) 283 mock_get_ssc.assert_called_once_with() 284 285 def test_update_ssc(self): 286 287 mock_ensure_shares_mounted = self.mock_object( 288 self.driver, '_ensure_shares_mounted') 289 mock_get_pool_map = self.mock_object( 290 self.driver, '_get_flexvol_to_pool_map', 291 return_value='fake_map') 292 mock_update_ssc = self.mock_object( 293 self.driver.ssc_library, 'update_ssc') 294 295 result = self.driver._update_ssc() 296 297 self.assertIsNone(result) 298 mock_ensure_shares_mounted.assert_called_once_with() 299 mock_get_pool_map.assert_called_once_with() 300 mock_update_ssc.assert_called_once_with('fake_map') 301 302 def test_get_pool_map(self): 303 304 self.driver.zapi_client = mock.Mock() 305 mock_get_operational_lif_addresses = self.mock_object( 306 self.driver.zapi_client, 'get_operational_lif_addresses', 307 return_value=[fake.SHARE_IP]) 308 mock_resolve_hostname = self.mock_object( 309 na_utils, 'resolve_hostname', return_value=fake.SHARE_IP) 310 mock_get_flexvol = self.mock_object( 311 self.driver.zapi_client, 'get_flexvol', 312 return_value={'name': fake.NETAPP_VOLUME}) 313 314 result = self.driver._get_flexvol_to_pool_map() 315 316 expected = { 317 fake.NETAPP_VOLUME: { 318 'pool_name': fake.NFS_SHARE, 319 }, 320 } 321 self.assertEqual(expected, result) 322 mock_get_operational_lif_addresses.assert_called_once_with() 323 mock_resolve_hostname.assert_called_once_with(fake.SHARE_IP) 324 mock_get_flexvol.assert_called_once_with(flexvol_path=fake.EXPORT_PATH) 325 326 def test_get_pool_map_address_not_found(self): 327 328 self.driver.zapi_client = mock.Mock() 329 self.mock_object(self.driver.zapi_client, 330 'get_operational_lif_addresses', 331 return_value=[]) 332 self.mock_object(na_utils, 333 'resolve_hostname', 334 return_value=fake.SHARE_IP) 335 336 result = self.driver._get_flexvol_to_pool_map() 337 338 self.assertEqual({}, result) 339 340 def test_get_pool_map_flexvol_not_found(self): 341 342 self.driver.zapi_client = mock.Mock() 343 self.mock_object(self.driver.zapi_client, 344 'get_operational_lif_addresses', 345 return_value=[fake.SHARE_IP]) 346 self.mock_object(na_utils, 347 'resolve_hostname', 348 return_value=fake.SHARE_IP) 349 side_effect = exception.VolumeBackendAPIException(data='fake_data') 350 self.mock_object(self.driver.zapi_client, 351 'get_flexvol', 352 side_effect=side_effect) 353 354 result = self.driver._get_flexvol_to_pool_map() 355 356 self.assertEqual({}, result) 357 358 @ddt.data(['/mnt/img-id1', '/mnt/img-id2'], []) 359 def test__shortlist_del_eligible_files(self, old_files): 360 self.driver.zapi_client = mock.Mock() 361 self.driver.zapi_client.get_file_usage = mock.Mock(return_value='1000') 362 mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug') 363 self.mock_object(self.driver, '_get_vserver_and_exp_vol', 364 return_value=('openstack', 'fake_share')) 365 expected_list = [(o, '1000') for o in old_files] 366 367 observed_list = self.driver._shortlist_del_eligible_files( 368 'fake_ip:fake_share', old_files) 369 370 self.assertEqual(expected_list, observed_list) 371 self.assertEqual(1, mock_debug_log.call_count) 372 373 @ddt.data({'ip': None, 'shares': None}, 374 {'ip': 'fake_ip', 'shares': ['fip:/fsh1']}) 375 @ddt.unpack 376 def test__share_match_for_ip_no_match(self, ip, shares): 377 def side_effect(arg): 378 if arg == 'fake_ip': 379 return 'openstack' 380 return None 381 382 self.mock_object(self.driver, '_get_vserver_for_ip', 383 side_effect=side_effect) 384 mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug') 385 386 retval = self.driver._share_match_for_ip(ip, shares) 387 388 self.assertIsNone(retval) 389 self.assertEqual(1, mock_debug_log.call_count) 390 391 def test__share_match_for_ip(self): 392 shares = ['fip:/fsh1'] 393 self.mock_object(self.driver, '_get_vserver_for_ip', 394 return_value='openstack') 395 mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug') 396 397 retval = self.driver._share_match_for_ip('fip', shares) 398 399 self.assertEqual('fip:/fsh1', retval) 400 self.assertEqual(1, mock_debug_log.call_count) 401 402 def test__get_vserver_for_ip_ignores_zapi_exception(self): 403 self.driver.zapi_client = mock.Mock() 404 self.driver.zapi_client.get_if_info_by_ip = mock.Mock( 405 side_effect=exception.NotFound) 406 407 vserver = self.driver._get_vserver_for_ip('FAKE_IP') 408 409 self.assertIsNone(vserver) 410 411 def test__get_vserver_for_ip(self): 412 self.driver.zapi_client = mock.Mock() 413 self.driver.zapi_client.get_if_info_by_ip = mock.Mock( 414 return_value=fake.get_fake_ifs()) 415 416 vserver = self.driver._get_vserver_for_ip('FAKE_IP') 417 418 self.assertIsNone(vserver) 419 420 def test_check_for_setup_error(self): 421 super_check_for_setup_error = self.mock_object( 422 nfs_base.NetAppNfsDriver, 'check_for_setup_error') 423 mock_add_looping_tasks = self.mock_object( 424 self.driver, '_add_looping_tasks') 425 426 self.driver.check_for_setup_error() 427 428 self.assertEqual(1, super_check_for_setup_error.call_count) 429 self.assertEqual(1, mock_add_looping_tasks.call_count) 430 mock_add_looping_tasks.assert_called_once_with() 431 432 @ddt.data({'replication_enabled': True, 'failed_over': False, 433 'cluster_credentials': True}, 434 {'replication_enabled': True, 'failed_over': True, 435 'cluster_credentials': True}, 436 {'replication_enabled': False, 'failed_over': False, 437 'cluster_credentials': False}) 438 @ddt.unpack 439 def test_handle_housekeeping_tasks( 440 self, replication_enabled, failed_over, cluster_credentials): 441 self.driver.using_cluster_credentials = cluster_credentials 442 ensure_mirrors = self.mock_object(data_motion.DataMotionMixin, 443 'ensure_snapmirrors') 444 self.mock_object(self.driver.ssc_library, 'get_ssc_flexvol_names', 445 return_value=fake_ssc.SSC.keys()) 446 mock_remove_unused_qos_policy_groups = self.mock_object( 447 self.driver.zapi_client, 'remove_unused_qos_policy_groups') 448 self.driver.replication_enabled = replication_enabled 449 self.driver.failed_over = failed_over 450 451 self.driver._handle_housekeeping_tasks() 452 453 if self.driver.using_cluster_credentials: 454 mock_remove_unused_qos_policy_groups.assert_called_once_with() 455 else: 456 mock_remove_unused_qos_policy_groups.assert_not_called() 457 458 if replication_enabled and not failed_over: 459 ensure_mirrors.assert_called_once_with( 460 self.driver.configuration, self.driver.backend_name, 461 fake_ssc.SSC.keys()) 462 else: 463 self.assertFalse(ensure_mirrors.called) 464 465 def test_handle_ems_logging(self): 466 467 volume_list = ['vol0', 'vol1', 'vol2'] 468 self.mock_object( 469 self.driver, '_get_backing_flexvol_names', 470 return_value=volume_list) 471 self.mock_object( 472 dot_utils, 'build_ems_log_message_0', 473 return_value='fake_base_ems_log_message') 474 self.mock_object( 475 dot_utils, 'build_ems_log_message_1', 476 return_value='fake_pool_ems_log_message') 477 mock_send_ems_log_message = self.mock_object( 478 self.driver.zapi_client, 'send_ems_log_message') 479 480 self.driver._handle_ems_logging() 481 482 mock_send_ems_log_message.assert_has_calls([ 483 mock.call('fake_base_ems_log_message'), 484 mock.call('fake_pool_ems_log_message'), 485 ]) 486 dot_utils.build_ems_log_message_0.assert_called_once_with( 487 self.driver.driver_name, self.driver.app_version) 488 dot_utils.build_ems_log_message_1.assert_called_once_with( 489 self.driver.driver_name, self.driver.app_version, 490 self.driver.vserver, volume_list, []) 491 492 def test_delete_volume(self): 493 fake_provider_location = 'fake_provider_location' 494 fake_volume = {'provider_location': fake_provider_location} 495 self.mock_object(self.driver, '_delete_backing_file_for_volume') 496 self.mock_object(na_utils, 497 'get_valid_qos_policy_group_info', 498 return_value='fake_qos_policy_group_info') 499 500 self.driver.delete_volume(fake_volume) 501 502 self.driver._delete_backing_file_for_volume.assert_called_once_with( 503 fake_volume) 504 na_utils.get_valid_qos_policy_group_info.assert_called_once_with( 505 fake_volume) 506 (self.driver.zapi_client.mark_qos_policy_group_for_deletion. 507 assert_called_once_with('fake_qos_policy_group_info')) 508 509 def test_delete_volume_exception_path(self): 510 fake_provider_location = 'fake_provider_location' 511 fake_volume = {'provider_location': fake_provider_location} 512 self.mock_object(self.driver, '_delete_backing_file_for_volume') 513 self.mock_object(na_utils, 514 'get_valid_qos_policy_group_info', 515 return_value='fake_qos_policy_group_info') 516 self.mock_object( 517 self.driver.zapi_client, 518 'mark_qos_policy_group_for_deletion', 519 side_effect=exception.NetAppDriverException) 520 521 self.driver.delete_volume(fake_volume) 522 523 self.driver._delete_backing_file_for_volume.assert_called_once_with( 524 fake_volume) 525 na_utils.get_valid_qos_policy_group_info.assert_called_once_with( 526 fake_volume) 527 (self.driver.zapi_client.mark_qos_policy_group_for_deletion. 528 assert_called_once_with('fake_qos_policy_group_info')) 529 530 def test_delete_backing_file_for_volume(self): 531 mock_filer_delete = self.mock_object(self.driver, '_delete_file') 532 mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver, 533 'delete_volume') 534 535 self.driver._delete_backing_file_for_volume(fake.NFS_VOLUME) 536 537 mock_filer_delete.assert_called_once_with( 538 fake.NFS_VOLUME['id'], fake.NFS_VOLUME['name']) 539 self.assertEqual(0, mock_super_delete.call_count) 540 541 @ddt.data(True, False) 542 def test_delete_backing_file_for_volume_exception_path(self, super_exc): 543 mock_exception_log = self.mock_object(nfs_cmode.LOG, 'exception') 544 exception_call_count = 2 if super_exc else 1 545 mock_filer_delete = self.mock_object(self.driver, '_delete_file') 546 mock_filer_delete.side_effect = [Exception] 547 mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver, 548 'delete_volume') 549 if super_exc: 550 mock_super_delete.side_effect = [Exception] 551 552 self.driver._delete_backing_file_for_volume(fake.NFS_VOLUME) 553 554 mock_filer_delete.assert_called_once_with( 555 fake.NFS_VOLUME['id'], fake.NFS_VOLUME['name']) 556 mock_super_delete.assert_called_once_with(fake.NFS_VOLUME) 557 self.assertEqual(exception_call_count, mock_exception_log.call_count) 558 559 def test_delete_snapshot(self): 560 mock_get_location = self.mock_object(self.driver, 561 '_get_provider_location') 562 mock_get_location.return_value = fake.PROVIDER_LOCATION 563 mock_delete_backing = self.mock_object( 564 self.driver, '_delete_backing_file_for_snapshot') 565 566 self.driver.delete_snapshot(fake.test_snapshot) 567 568 mock_delete_backing.assert_called_once_with(fake.test_snapshot) 569 570 def test_delete_backing_file_for_snapshot(self): 571 mock_filer_delete = self.mock_object(self.driver, '_delete_file') 572 mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver, 573 'delete_snapshot') 574 575 self.driver._delete_backing_file_for_snapshot(fake.test_snapshot) 576 577 mock_filer_delete.assert_called_once_with( 578 fake.test_snapshot['volume_id'], fake.test_snapshot['name']) 579 self.assertEqual(0, mock_super_delete.call_count) 580 581 @ddt.data(True, False) 582 def test_delete_backing_file_for_snapshot_exception_path(self, super_exc): 583 mock_exception_log = self.mock_object(nfs_cmode.LOG, 'exception') 584 exception_call_count = 2 if super_exc else 1 585 mock_filer_delete = self.mock_object(self.driver, '_delete_file') 586 mock_filer_delete.side_effect = [Exception] 587 mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver, 588 'delete_snapshot') 589 if super_exc: 590 mock_super_delete.side_effect = [Exception] 591 592 self.driver._delete_backing_file_for_snapshot(fake.test_snapshot) 593 594 mock_filer_delete.assert_called_once_with( 595 fake.test_snapshot['volume_id'], fake.test_snapshot['name']) 596 mock_super_delete.assert_called_once_with(fake.test_snapshot) 597 self.assertEqual(exception_call_count, mock_exception_log.call_count) 598 599 def test_delete_file(self): 600 mock_get_vs_ip = self.mock_object(self.driver, '_get_export_ip_path') 601 mock_get_vs_ip.return_value = (fake.SHARE_IP, fake.EXPORT_PATH) 602 mock_get_vserver = self.mock_object(self.driver, '_get_vserver_for_ip') 603 mock_get_vserver.return_value = fake.VSERVER_NAME 604 mock_zapi_get_vol = self.driver.zapi_client.get_vol_by_junc_vserver 605 mock_zapi_get_vol.return_value = fake.FLEXVOL 606 mock_zapi_delete = self.driver.zapi_client.delete_file 607 608 self.driver._delete_file( 609 fake.test_snapshot['volume_id'], fake.test_snapshot['name']) 610 611 mock_get_vs_ip.assert_called_once_with( 612 volume_id=fake.test_snapshot['volume_id']) 613 mock_get_vserver.assert_called_once_with(fake.SHARE_IP) 614 mock_zapi_get_vol.assert_called_once_with( 615 fake.VSERVER_NAME, fake.EXPORT_PATH) 616 mock_zapi_delete.assert_called_once_with( 617 '/vol/%s/%s' % (fake.FLEXVOL, fake.test_snapshot['name'])) 618 619 def test_do_qos_for_volume_no_exception(self): 620 621 mock_get_info = self.mock_object(na_utils, 622 'get_valid_qos_policy_group_info') 623 mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO 624 mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group 625 mock_set_policy = self.mock_object(self.driver, 626 '_set_qos_policy_group_on_volume') 627 mock_error_log = self.mock_object(nfs_cmode.LOG, 'error') 628 mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug') 629 mock_cleanup = self.mock_object(self.driver, 630 '_cleanup_volume_on_failure') 631 632 self.driver._do_qos_for_volume(fake.NFS_VOLUME, fake.EXTRA_SPECS) 633 634 mock_get_info.assert_has_calls([ 635 mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)]) 636 mock_provision_qos.assert_has_calls([ 637 mock.call(fake.QOS_POLICY_GROUP_INFO)]) 638 mock_set_policy.assert_has_calls([ 639 mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO)]) 640 self.assertEqual(0, mock_error_log.call_count) 641 self.assertEqual(0, mock_debug_log.call_count) 642 self.assertEqual(0, mock_cleanup.call_count) 643 644 def test_do_qos_for_volume_exception_w_cleanup(self): 645 mock_get_info = self.mock_object(na_utils, 646 'get_valid_qos_policy_group_info') 647 mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO 648 mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group 649 mock_set_policy = self.mock_object(self.driver, 650 '_set_qos_policy_group_on_volume') 651 mock_set_policy.side_effect = netapp_api.NaApiError 652 mock_error_log = self.mock_object(nfs_cmode.LOG, 'error') 653 mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug') 654 mock_cleanup = self.mock_object(self.driver, 655 '_cleanup_volume_on_failure') 656 657 self.assertRaises(netapp_api.NaApiError, 658 self.driver._do_qos_for_volume, 659 fake.NFS_VOLUME, 660 fake.EXTRA_SPECS) 661 662 mock_get_info.assert_has_calls([ 663 mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)]) 664 mock_provision_qos.assert_has_calls([ 665 mock.call(fake.QOS_POLICY_GROUP_INFO)]) 666 mock_set_policy.assert_has_calls([ 667 mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO)]) 668 self.assertEqual(1, mock_error_log.call_count) 669 self.assertEqual(1, mock_debug_log.call_count) 670 mock_cleanup.assert_has_calls([ 671 mock.call(fake.NFS_VOLUME)]) 672 673 def test_do_qos_for_volume_exception_no_cleanup(self): 674 675 mock_get_info = self.mock_object(na_utils, 676 'get_valid_qos_policy_group_info') 677 mock_get_info.side_effect = exception.Invalid 678 mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group 679 mock_set_policy = self.mock_object(self.driver, 680 '_set_qos_policy_group_on_volume') 681 mock_error_log = self.mock_object(nfs_cmode.LOG, 'error') 682 mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug') 683 mock_cleanup = self.mock_object(self.driver, 684 '_cleanup_volume_on_failure') 685 686 self.assertRaises(exception.Invalid, self.driver._do_qos_for_volume, 687 fake.NFS_VOLUME, fake.EXTRA_SPECS, cleanup=False) 688 689 mock_get_info.assert_has_calls([ 690 mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)]) 691 self.assertEqual(0, mock_provision_qos.call_count) 692 self.assertEqual(0, mock_set_policy.call_count) 693 self.assertEqual(1, mock_error_log.call_count) 694 self.assertEqual(0, mock_debug_log.call_count) 695 self.assertEqual(0, mock_cleanup.call_count) 696 697 def test_set_qos_policy_group_on_volume(self): 698 699 mock_get_name_from_info = self.mock_object( 700 na_utils, 'get_qos_policy_group_name_from_info') 701 mock_get_name_from_info.return_value = fake.QOS_POLICY_GROUP_NAME 702 703 mock_extract_host = self.mock_object(volume_utils, 'extract_host') 704 mock_extract_host.return_value = fake.NFS_SHARE 705 706 mock_get_flex_vol_name =\ 707 self.driver.zapi_client.get_vol_by_junc_vserver 708 mock_get_flex_vol_name.return_value = fake.FLEXVOL 709 710 mock_file_assign_qos = self.driver.zapi_client.file_assign_qos 711 712 self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME, 713 fake.QOS_POLICY_GROUP_INFO) 714 715 mock_get_name_from_info.assert_has_calls([ 716 mock.call(fake.QOS_POLICY_GROUP_INFO)]) 717 mock_extract_host.assert_has_calls([ 718 mock.call(fake.NFS_HOST_STRING, level='pool')]) 719 mock_get_flex_vol_name.assert_has_calls([ 720 mock.call(fake.VSERVER_NAME, fake.EXPORT_PATH)]) 721 mock_file_assign_qos.assert_has_calls([ 722 mock.call(fake.FLEXVOL, fake.QOS_POLICY_GROUP_NAME, 723 fake.NFS_VOLUME['name'])]) 724 725 def test_set_qos_policy_group_on_volume_no_info(self): 726 727 mock_get_name_from_info = self.mock_object( 728 na_utils, 'get_qos_policy_group_name_from_info') 729 730 mock_extract_host = self.mock_object(volume_utils, 'extract_host') 731 732 mock_get_flex_vol_name =\ 733 self.driver.zapi_client.get_vol_by_junc_vserver 734 735 mock_file_assign_qos = self.driver.zapi_client.file_assign_qos 736 737 self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME, 738 None) 739 740 self.assertEqual(0, mock_get_name_from_info.call_count) 741 self.assertEqual(0, mock_extract_host.call_count) 742 self.assertEqual(0, mock_get_flex_vol_name.call_count) 743 self.assertEqual(0, mock_file_assign_qos.call_count) 744 745 def test_set_qos_policy_group_on_volume_no_name(self): 746 747 mock_get_name_from_info = self.mock_object( 748 na_utils, 'get_qos_policy_group_name_from_info') 749 mock_get_name_from_info.return_value = None 750 751 mock_extract_host = self.mock_object(volume_utils, 'extract_host') 752 753 mock_get_flex_vol_name =\ 754 self.driver.zapi_client.get_vol_by_junc_vserver 755 756 mock_file_assign_qos = self.driver.zapi_client.file_assign_qos 757 758 self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME, 759 fake.QOS_POLICY_GROUP_INFO) 760 761 mock_get_name_from_info.assert_has_calls([ 762 mock.call(fake.QOS_POLICY_GROUP_INFO)]) 763 self.assertEqual(0, mock_extract_host.call_count) 764 self.assertEqual(0, mock_get_flex_vol_name.call_count) 765 self.assertEqual(0, mock_file_assign_qos.call_count) 766 767 @ddt.data({'share': None, 'is_snapshot': False}, 768 {'share': None, 'is_snapshot': True}, 769 {'share': 'fake_share', 'is_snapshot': False}, 770 {'share': 'fake_share', 'is_snapshot': True}) 771 @ddt.unpack 772 def test_clone_backing_file_for_volume(self, share, is_snapshot): 773 774 mock_get_vserver_and_exp_vol = self.mock_object( 775 self.driver, '_get_vserver_and_exp_vol', 776 return_value=(fake.VSERVER_NAME, fake.FLEXVOL)) 777 778 self.driver._clone_backing_file_for_volume( 779 fake.FLEXVOL, 'fake_clone', fake.VOLUME_ID, share=share, 780 is_snapshot=is_snapshot) 781 782 mock_get_vserver_and_exp_vol.assert_called_once_with( 783 fake.VOLUME_ID, share) 784 self.driver.zapi_client.clone_file.assert_called_once_with( 785 fake.FLEXVOL, fake.FLEXVOL, 'fake_clone', fake.VSERVER_NAME, 786 is_snapshot=is_snapshot) 787 788 def test__clone_backing_file_for_volume(self): 789 body = fake.get_fake_net_interface_get_iter_response() 790 self.driver.zapi_client.get_if_info_by_ip = mock.Mock( 791 return_value=[netapp_api.NaElement(body)]) 792 self.driver.zapi_client.get_vol_by_junc_vserver = mock.Mock( 793 return_value='nfsvol') 794 self.mock_object(self.driver, '_get_export_ip_path', 795 return_value=('127.0.0.1', 'fakepath')) 796 797 retval = self.driver._clone_backing_file_for_volume( 798 'vol', 'clone', 'vol_id', share='share', is_snapshot=True) 799 800 self.assertIsNone(retval) 801 self.driver.zapi_client.clone_file.assert_called_once_with( 802 'nfsvol', 'vol', 'clone', None, is_snapshot=True) 803 804 def test_copy_from_img_service_copyoffload_nonexistent_binary_path(self): 805 self.mock_object(nfs_cmode.LOG, 'debug') 806 drv = self.driver 807 context = object() 808 volume = {'id': 'vol_id', 'name': 'name', 809 'host': 'openstack@nfscmode#192.128.1.1:/mnt_point'} 810 image_service = mock.Mock() 811 image_service.get_location.return_value = (mock.Mock(), mock.Mock()) 812 image_service.show.return_value = {'size': 0} 813 image_id = 'image_id' 814 drv._client = mock.Mock() 815 drv._client.get_api_version = mock.Mock(return_value=(1, 20)) 816 nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock( 817 return_value=[]) 818 drv._construct_image_nfs_url = mock.Mock(return_value=["nfs://1"]) 819 drv._check_get_nfs_path_segs = mock.Mock( 820 return_value=("test:test", "dr")) 821 drv._get_ip_verify_on_cluster = mock.Mock(return_value="192.128.1.1") 822 drv._get_mount_point_for_share = mock.Mock(return_value='mnt_point') 823 drv._check_share_can_hold_size = mock.Mock() 824 # Raise error as if the copyoffload file can not be found 825 drv._clone_file_dst_exists = mock.Mock(side_effect=OSError()) 826 drv._discover_file_till_timeout = mock.Mock() 827 828 # Verify the original error is propagated 829 self.assertRaises(OSError, drv._copy_from_img_service, 830 context, volume, image_service, image_id) 831 832 drv._discover_file_till_timeout.assert_not_called() 833 834 @mock.patch.object(image_utils, 'qemu_img_info') 835 def test_copy_from_img_service_raw_copyoffload_workflow_success( 836 self, mock_qemu_img_info): 837 drv = self.driver 838 volume = {'id': 'vol_id', 'name': 'name', 'size': 1, 839 'host': 'openstack@nfscmode#ip1:/mnt_point'} 840 image_id = 'image_id' 841 context = object() 842 image_service = mock.Mock() 843 image_service.get_location.return_value = ('nfs://ip1/openstack/img', 844 None) 845 image_service.show.return_value = {'size': 1, 'disk_format': 'raw'} 846 847 drv._check_get_nfs_path_segs =\ 848 mock.Mock(return_value=('ip1', '/openstack')) 849 drv._get_ip_verify_on_cluster = mock.Mock(return_value='ip1') 850 drv._get_host_ip = mock.Mock(return_value='ip2') 851 drv._get_export_path = mock.Mock(return_value='/exp_path') 852 drv._get_provider_location = mock.Mock(return_value='share') 853 drv._execute = mock.Mock() 854 drv._get_mount_point_for_share = mock.Mock(return_value='mnt_point') 855 drv._discover_file_till_timeout = mock.Mock(return_value=True) 856 img_inf = mock.Mock() 857 img_inf.file_format = 'raw' 858 mock_qemu_img_info.return_value = img_inf 859 drv._check_share_can_hold_size = mock.Mock() 860 drv._move_nfs_file = mock.Mock(return_value=True) 861 drv._delete_file_at_path = mock.Mock() 862 drv._clone_file_dst_exists = mock.Mock() 863 drv._post_clone_image = mock.Mock() 864 865 retval = drv._copy_from_img_service( 866 context, volume, image_service, image_id) 867 868 self.assertTrue(retval) 869 drv._get_ip_verify_on_cluster.assert_any_call('ip1') 870 drv._check_share_can_hold_size.assert_called_with( 871 'ip1:/mnt_point', 1) 872 self.assertEqual(1, drv._execute.call_count) 873 874 @mock.patch.object(image_utils, 'convert_image') 875 @mock.patch.object(image_utils, 'qemu_img_info') 876 @mock.patch('os.path.exists') 877 def test_copy_from_img_service_qcow2_copyoffload_workflow_success( 878 self, mock_exists, mock_qemu_img_info, mock_cvrt_image): 879 drv = self.driver 880 cinder_mount_point_base = '/opt/stack/data/cinder/mnt/' 881 # To get the cinder mount point directory, we use: 882 mount_dir = hashlib.md5( 883 '203.0.113.122:/cinder-flexvol1'.encode('utf-8')).hexdigest() 884 cinder_mount_point = cinder_mount_point_base + mount_dir 885 destination_copied_file = ( 886 '/cinder-flexvol1/a155308c-0290-497b-b278-4cdd01de0253' 887 ) 888 volume = {'id': 'vol_id', 'name': 'name', 'size': 1, 889 'host': 'openstack@nfscmode#203.0.113.122:/cinder-flexvol1'} 890 image_id = 'image_id' 891 context = object() 892 image_service = mock.Mock() 893 image_service.get_location.return_value = ( 894 'nfs://203.0.113.122/glance-flexvol1', None) 895 image_service.show.return_value = {'size': 1, 896 'disk_format': 'qcow2'} 897 drv._check_get_nfs_path_segs = ( 898 mock.Mock(return_value=('203.0.113.122', '/openstack')) 899 ) 900 901 drv._get_ip_verify_on_cluster = mock.Mock(return_value='203.0.113.122') 902 drv._execute = mock.Mock() 903 drv._execute_as_root = False 904 drv._get_mount_point_for_share = mock.Mock( 905 return_value=cinder_mount_point) 906 img_inf = mock.Mock() 907 img_inf.file_format = 'raw' 908 mock_qemu_img_info.return_value = img_inf 909 drv._check_share_can_hold_size = mock.Mock() 910 911 drv._move_nfs_file = mock.Mock(return_value=True) 912 drv._delete_file_at_path = mock.Mock() 913 drv._clone_file_dst_exists = mock.Mock() 914 drv._post_clone_image = mock.Mock() 915 self.mock_object(uuid, 'uuid4', mock.Mock( 916 return_value='a155308c-0290-497b-b278-4cdd01de0253')) 917 918 retval = drv._copy_from_img_service( 919 context, volume, image_service, image_id) 920 921 self.assertTrue(retval) 922 drv._get_ip_verify_on_cluster.assert_any_call('203.0.113.122') 923 drv._check_share_can_hold_size.assert_called_with( 924 '203.0.113.122:/cinder-flexvol1', 1) 925 926 # _execute must be called once for copy-offload and again to touch 927 # the top directory to refresh cache 928 drv._execute.assert_has_calls( 929 [ 930 mock.call( 931 'copyoffload_tool_path', '203.0.113.122', 932 '203.0.113.122', '/openstack/glance-flexvol1', 933 destination_copied_file, run_as_root=False, 934 check_exit_code=0 935 ), 936 mock.call('touch', cinder_mount_point, run_as_root=False) 937 ] 938 ) 939 self.assertEqual(2, drv._execute.call_count) 940 self.assertEqual(2, drv._delete_file_at_path.call_count) 941 self.assertEqual(1, drv._clone_file_dst_exists.call_count) 942 943 def test_copy_from_cache_copyoffload_success(self): 944 drv = self.driver 945 volume = {'id': 'vol_id', 'name': 'name', 'size': 1, 946 'host': 'openstack@nfscmode#192.128.1.1:/exp_path'} 947 image_id = 'image_id' 948 cache_result = [('ip1:/openstack', 'img-cache-imgid')] 949 drv._get_ip_verify_on_cluster = mock.Mock(return_value='ip1') 950 drv._execute = mock.Mock() 951 drv._register_image_in_cache = mock.Mock() 952 drv._post_clone_image = mock.Mock() 953 954 copied = drv._copy_from_cache(volume, image_id, cache_result) 955 956 self.assertTrue(copied) 957 drv._get_ip_verify_on_cluster.assert_any_call('ip1') 958 drv._execute.assert_called_once_with( 959 'copyoffload_tool_path', 'ip1', 'ip1', 960 '/openstack/img-cache-imgid', '/exp_path/name', 961 run_as_root=False, check_exit_code=0) 962 963 def test_unmanage(self): 964 mock_get_info = self.mock_object(na_utils, 965 'get_valid_qos_policy_group_info') 966 mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO 967 968 mock_mark_for_deletion =\ 969 self.driver.zapi_client.mark_qos_policy_group_for_deletion 970 971 super_unmanage = self.mock_object(nfs_base.NetAppNfsDriver, 'unmanage') 972 973 self.driver.unmanage(fake.NFS_VOLUME) 974 975 mock_get_info.assert_has_calls([mock.call(fake.NFS_VOLUME)]) 976 mock_mark_for_deletion.assert_has_calls([ 977 mock.call(fake.QOS_POLICY_GROUP_INFO)]) 978 super_unmanage.assert_has_calls([mock.call(fake.NFS_VOLUME)]) 979 980 def test_unmanage_invalid_qos(self): 981 mock_get_info = self.mock_object(na_utils, 982 'get_valid_qos_policy_group_info') 983 mock_get_info.side_effect = exception.Invalid 984 985 super_unmanage = self.mock_object(nfs_base.NetAppNfsDriver, 'unmanage') 986 987 self.driver.unmanage(fake.NFS_VOLUME) 988 989 mock_get_info.assert_has_calls([mock.call(fake.NFS_VOLUME)]) 990 super_unmanage.assert_has_calls([mock.call(fake.NFS_VOLUME)]) 991 992 def test_add_looping_tasks(self): 993 mock_update_ssc = self.mock_object(self.driver, '_update_ssc') 994 mock_handle_housekeeping = self.mock_object( 995 self.driver, '_handle_housekeeping_tasks') 996 mock_add_task = self.mock_object(self.driver.loopingcalls, 'add_task') 997 mock_super_add_looping_tasks = self.mock_object( 998 nfs_base.NetAppNfsDriver, '_add_looping_tasks') 999 1000 self.driver._add_looping_tasks() 1001 1002 mock_update_ssc.assert_called_once_with() 1003 mock_add_task.assert_has_calls([ 1004 mock.call(mock_update_ssc, 1005 loopingcalls.ONE_HOUR, 1006 loopingcalls.ONE_HOUR), 1007 mock.call(mock_handle_housekeeping, 1008 loopingcalls.TEN_MINUTES, 1009 0)]) 1010 mock_super_add_looping_tasks.assert_called_once_with() 1011 1012 @ddt.data({'type_match': True, 'expected': True}, 1013 {'type_match': False, 'expected': False}) 1014 @ddt.unpack 1015 def test_is_share_clone_compatible(self, type_match, expected): 1016 1017 mock_get_flexvol_name_for_share = self.mock_object( 1018 self.driver, '_get_flexvol_name_for_share', 1019 return_value='fake_flexvol') 1020 mock_is_share_vol_type_match = self.mock_object( 1021 self.driver, '_is_share_vol_type_match', return_value=type_match) 1022 1023 result = self.driver._is_share_clone_compatible(fake.VOLUME, 1024 fake.NFS_SHARE) 1025 1026 self.assertEqual(expected, result) 1027 mock_get_flexvol_name_for_share.assert_called_once_with(fake.NFS_SHARE) 1028 mock_is_share_vol_type_match.assert_called_once() 1029 1030 @ddt.data({'flexvols': ['volume1', 'volume2'], 'expected': True}, 1031 {'flexvols': ['volume3', 'volume4'], 'expected': False}, 1032 {'flexvols': [], 'expected': False}) 1033 @ddt.unpack 1034 def test_is_share_vol_type_match(self, flexvols, expected): 1035 1036 mock_get_volume_extra_specs = self.mock_object( 1037 na_utils, 'get_volume_extra_specs', 1038 return_value='fake_extra_specs') 1039 mock_get_matching_flexvols_for_extra_specs = self.mock_object( 1040 self.driver.ssc_library, 'get_matching_flexvols_for_extra_specs', 1041 return_value=flexvols) 1042 1043 result = self.driver._is_share_vol_type_match(fake.VOLUME, 1044 fake.NFS_SHARE, 1045 'volume1') 1046 1047 self.assertEqual(expected, result) 1048 mock_get_volume_extra_specs.assert_called_once_with(fake.VOLUME) 1049 mock_get_matching_flexvols_for_extra_specs.assert_called_once_with( 1050 'fake_extra_specs') 1051 1052 @ddt.data({'share': 'volume1', 'expected': 'volume1'}, 1053 {'share': 'volume3', 'expected': None}) 1054 @ddt.unpack 1055 def test_get_flexvol_name_for_share(self, share, expected): 1056 1057 mock_get_ssc = self.mock_object( 1058 self.driver.ssc_library, 'get_ssc', return_value=fake_ssc.SSC) 1059 1060 result = self.driver._get_flexvol_name_for_share(share) 1061 1062 self.assertEqual(expected, result) 1063 mock_get_ssc.assert_called_once_with() 1064 1065 def test_get_flexvol_name_for_share_no_ssc_vols(self): 1066 1067 mock_get_ssc = self.mock_object( 1068 self.driver.ssc_library, 'get_ssc', return_value={}) 1069 1070 result = self.driver._get_flexvol_name_for_share('fake_share') 1071 1072 self.assertIsNone(result) 1073 mock_get_ssc.assert_called_once_with() 1074 1075 def test_find_image_location_with_local_copy(self): 1076 local_share = '/share' 1077 cache_result = [ 1078 ('ip1:/openstack', 'img-cache-imgid'), 1079 ('ip2:/openstack', 'img-cache-imgid'), 1080 (local_share, 'img-cache-imgid'), 1081 ('ip3:/openstack', 'img-cache-imgid'), 1082 ] 1083 1084 mock_extract_host = self.mock_object(volume_utils, 'extract_host') 1085 mock_extract_host.return_value = local_share 1086 1087 cache_copy, found_local_copy = self.driver._find_image_location( 1088 cache_result, fake.VOLUME) 1089 1090 self.assertEqual(cache_result[2], cache_copy) 1091 self.assertTrue(found_local_copy) 1092 1093 def test_find_image_location_with_remote_copy(self): 1094 cache_result = [('ip1:/openstack', 'img-cache-imgid')] 1095 1096 mock_extract_host = self.mock_object(volume_utils, 'extract_host') 1097 mock_extract_host.return_value = '/share' 1098 1099 cache_copy, found_local_copy = self.driver._find_image_location( 1100 cache_result, fake.VOLUME) 1101 1102 self.assertEqual(cache_result[0], cache_copy) 1103 self.assertFalse(found_local_copy) 1104 1105 def test_find_image_location_without_cache_copy(self): 1106 cache_result = [] 1107 mock_extract_host = self.mock_object(volume_utils, 'extract_host') 1108 mock_extract_host.return_value = '/share' 1109 1110 cache_copy, found_local_copy = self.driver._find_image_location( 1111 cache_result, fake.VOLUME) 1112 1113 self.assertIsNone(cache_copy) 1114 self.assertFalse(found_local_copy) 1115 1116 def test_clone_file_dest_exists(self): 1117 self.driver._get_vserver_and_exp_vol = mock.Mock( 1118 return_value=(fake.VSERVER_NAME, fake.EXPORT_PATH)) 1119 self.driver.zapi_client.clone_file = mock.Mock() 1120 1121 self.driver._clone_file_dst_exists( 1122 fake.NFS_SHARE, fake.IMAGE_FILE_ID, fake.VOLUME['name'], 1123 dest_exists=True) 1124 1125 self.driver._get_vserver_and_exp_vol.assert_called_once_with( 1126 share=fake.NFS_SHARE) 1127 self.driver.zapi_client.clone_file.assert_called_once_with( 1128 fake.EXPORT_PATH, fake.IMAGE_FILE_ID, fake.VOLUME['name'], 1129 fake.VSERVER_NAME, dest_exists=True) 1130 1131 @ddt.data((fake.NFS_SHARE, fake.SHARE_IP), 1132 (fake.NFS_SHARE_IPV6, fake.IPV6_ADDRESS)) 1133 @ddt.unpack 1134 def test_get_source_ip_and_path(self, share, ip): 1135 self.driver._get_ip_verify_on_cluster = mock.Mock( 1136 return_value=ip) 1137 1138 src_ip, src_path = self.driver._get_source_ip_and_path( 1139 share, fake.IMAGE_FILE_ID) 1140 1141 self.assertEqual(ip, src_ip) 1142 assert_path = fake.EXPORT_PATH + '/' + fake.IMAGE_FILE_ID 1143 self.assertEqual(assert_path, src_path) 1144 self.driver._get_ip_verify_on_cluster.assert_called_once_with(ip) 1145 1146 def test_get_destination_ip_and_path(self): 1147 self.driver._get_ip_verify_on_cluster = mock.Mock( 1148 return_value=fake.SHARE_IP) 1149 mock_extract_host = self.mock_object(volume_utils, 'extract_host') 1150 mock_extract_host.return_value = fake.NFS_SHARE 1151 1152 dest_ip, dest_path = self.driver._get_destination_ip_and_path( 1153 fake.VOLUME) 1154 1155 self.assertEqual(fake.SHARE_IP, dest_ip) 1156 assert_path = fake.EXPORT_PATH + '/' + fake.LUN_NAME 1157 self.assertEqual(assert_path, dest_path) 1158 self.driver._get_ip_verify_on_cluster.assert_called_once_with( 1159 fake.SHARE_IP) 1160 1161 def test_clone_image_copyoffload_from_cache_success(self): 1162 drv = self.driver 1163 context = object() 1164 volume = {'id': 'vol_id', 'name': 'name', 1165 'host': 'openstack@nfscmode#192.128.1.1:/mnt_point'} 1166 image_service = object() 1167 image_location = 'img-loc' 1168 image_id = 'image_id' 1169 image_meta = {'id': image_id} 1170 drv.zapi_client = mock.Mock() 1171 drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20)) 1172 nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock( 1173 return_value=[('share', 'img')]) 1174 nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock( 1175 return_value=False) 1176 drv._copy_from_cache = mock.Mock(return_value=True) 1177 1178 drv.clone_image(context, volume, image_location, image_meta, 1179 image_service) 1180 1181 drv._copy_from_cache.assert_called_once_with( 1182 volume, image_id, [('share', 'img')]) 1183 1184 def test_clone_image_copyoffload_from_img_service(self): 1185 drv = self.driver 1186 context = object() 1187 volume = {'id': 'vol_id', 'name': 'name', 1188 'host': 'openstack@nfscmode#192.128.1.1:/mnt_point', 1189 'provider_location': '192.128.1.1:/mnt_point'} 1190 image_service = object() 1191 image_id = 'image_id' 1192 image_meta = {'id': image_id} 1193 image_location = 'img-loc' 1194 drv.zapi_client = mock.Mock() 1195 drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20)) 1196 nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock( 1197 return_value=[]) 1198 nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock( 1199 return_value=False) 1200 nfs_base.NetAppNfsDriver._post_clone_image = mock.Mock( 1201 return_value=True) 1202 drv._copy_from_img_service = mock.Mock(return_value=True) 1203 1204 retval = drv.clone_image( 1205 context, volume, image_location, image_meta, image_service) 1206 1207 self.assertEqual(retval, ( 1208 {'provider_location': '192.128.1.1:/mnt_point', 1209 'bootable': True}, True)) 1210 drv._copy_from_img_service.assert_called_once_with( 1211 context, volume, image_service, image_id) 1212 1213 def test_clone_image_copyoffload_failure(self): 1214 mock_log = self.mock_object(nfs_cmode, 'LOG') 1215 drv = self.driver 1216 context = object() 1217 volume = {'id': 'vol_id', 'name': 'name'} 1218 image_service = object() 1219 image_id = 'image_id' 1220 image_meta = {'id': image_id} 1221 image_location = 'img-loc' 1222 drv.zapi_client = mock.Mock() 1223 drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20)) 1224 nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock( 1225 return_value=[]) 1226 nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock( 1227 return_value=False) 1228 drv._copy_from_img_service = mock.Mock(side_effect=Exception()) 1229 1230 retval = drv.clone_image( 1231 context, volume, image_location, image_meta, image_service) 1232 1233 self.assertEqual(retval, ({'bootable': False, 1234 'provider_location': None}, False)) 1235 drv._copy_from_img_service.assert_called_once_with( 1236 context, volume, image_service, image_id) 1237 mock_log.info.assert_not_called() 1238 1239 def test_copy_from_remote_cache(self): 1240 source_ip = '192.0.1.1' 1241 source_path = '/openstack/img-cache-imgid' 1242 cache_copy = ('192.0.1.1:/openstack', fake.IMAGE_FILE_ID) 1243 dest_path = fake.EXPORT_PATH + '/' + fake.VOLUME['name'] 1244 self.driver._execute = mock.Mock() 1245 self.driver._get_source_ip_and_path = mock.Mock( 1246 return_value=(source_ip, source_path)) 1247 self.driver._get_destination_ip_and_path = mock.Mock( 1248 return_value=(fake.SHARE_IP, dest_path)) 1249 self.driver._register_image_in_cache = mock.Mock() 1250 1251 self.driver._copy_from_remote_cache( 1252 fake.VOLUME, fake.IMAGE_FILE_ID, cache_copy) 1253 1254 self.driver._execute.assert_called_once_with( 1255 'copyoffload_tool_path', source_ip, fake.SHARE_IP, 1256 source_path, dest_path, run_as_root=False, check_exit_code=0) 1257 self.driver._get_source_ip_and_path.assert_called_once_with( 1258 cache_copy[0], fake.IMAGE_FILE_ID) 1259 self.driver._get_destination_ip_and_path.assert_called_once_with( 1260 fake.VOLUME) 1261 self.driver._register_image_in_cache.assert_called_once_with( 1262 fake.VOLUME, fake.IMAGE_FILE_ID) 1263 1264 def test_copy_from_cache_workflow_remote_location(self): 1265 cache_result = [('ip1:/openstack', fake.IMAGE_FILE_ID), 1266 ('ip2:/openstack', fake.IMAGE_FILE_ID), 1267 ('ip3:/openstack', fake.IMAGE_FILE_ID)] 1268 self.driver._find_image_location = mock.Mock(return_value=[ 1269 cache_result[0], False]) 1270 self.driver._copy_from_remote_cache = mock.Mock() 1271 self.driver._post_clone_image = mock.Mock() 1272 1273 copied = self.driver._copy_from_cache( 1274 fake.VOLUME, fake.IMAGE_FILE_ID, cache_result) 1275 1276 self.assertTrue(copied) 1277 self.driver._copy_from_remote_cache.assert_called_once_with( 1278 fake.VOLUME, fake.IMAGE_FILE_ID, cache_result[0]) 1279 1280 def test_copy_from_cache_workflow_remote_location_no_copyoffload(self): 1281 cache_result = [('ip1:/openstack', fake.IMAGE_FILE_ID), 1282 ('ip2:/openstack', fake.IMAGE_FILE_ID), 1283 ('ip3:/openstack', fake.IMAGE_FILE_ID)] 1284 self.driver._find_image_location = mock.Mock(return_value=[ 1285 cache_result[0], False]) 1286 self.driver._copy_from_remote_cache = mock.Mock() 1287 self.driver._post_clone_image = mock.Mock() 1288 self.driver.configuration.netapp_copyoffload_tool_path = None 1289 1290 copied = self.driver._copy_from_cache( 1291 fake.VOLUME, fake.IMAGE_FILE_ID, cache_result) 1292 1293 self.assertFalse(copied) 1294 self.driver._copy_from_remote_cache.assert_not_called() 1295 1296 def test_copy_from_cache_workflow_local_location(self): 1297 local_share = '/share' 1298 cache_result = [ 1299 ('ip1:/openstack', 'img-cache-imgid'), 1300 ('ip2:/openstack', 'img-cache-imgid'), 1301 (local_share, 'img-cache-imgid'), 1302 ('ip3:/openstack', 'img-cache-imgid'), 1303 ] 1304 self.driver._find_image_location = mock.Mock(return_value=[ 1305 cache_result[2], True]) 1306 self.driver._clone_file_dst_exists = mock.Mock() 1307 self.driver._post_clone_image = mock.Mock() 1308 1309 copied = self.driver._copy_from_cache( 1310 fake.VOLUME, fake.IMAGE_FILE_ID, cache_result) 1311 1312 self.assertTrue(copied) 1313 self.driver._clone_file_dst_exists.assert_called_once_with( 1314 local_share, fake.IMAGE_FILE_ID, fake.VOLUME['name'], 1315 dest_exists=True) 1316 1317 def test_copy_from_cache_workflow_no_location(self): 1318 cache_result = [] 1319 self.driver._find_image_location = mock.Mock( 1320 return_value=(None, False)) 1321 1322 copied = self.driver._copy_from_cache( 1323 fake.VOLUME, fake.IMAGE_FILE_ID, cache_result) 1324 1325 self.assertFalse(copied) 1326 1327 def test_copy_from_cache_workflow_exception(self): 1328 cache_result = [('ip1:/openstack', fake.IMAGE_FILE_ID)] 1329 self.driver._find_image_location = mock.Mock(return_value=[ 1330 cache_result[0], False]) 1331 self.driver._copy_from_remote_cache = mock.Mock( 1332 side_effect=Exception) 1333 self.driver._post_clone_image = mock.Mock() 1334 1335 copied = self.driver._copy_from_cache( 1336 fake.VOLUME, fake.IMAGE_FILE_ID, cache_result) 1337 1338 self.assertFalse(copied) 1339 self.driver._copy_from_remote_cache.assert_called_once_with( 1340 fake.VOLUME, fake.IMAGE_FILE_ID, cache_result[0]) 1341 self.assertFalse(self.driver._post_clone_image.called) 1342 1343 @ddt.data({'secondary_id': 'dev0', 'configured_targets': ['dev1']}, 1344 {'secondary_id': 'dev3', 'configured_targets': ['dev1', 'dev2']}, 1345 {'secondary_id': 'dev1', 'configured_targets': []}, 1346 {'secondary_id': None, 'configured_targets': []}) 1347 @ddt.unpack 1348 def test_failover_host_invalid_replication_target(self, secondary_id, 1349 configured_targets): 1350 """This tests executes a method in the DataMotionMixin.""" 1351 self.driver.backend_name = 'dev0' 1352 self.mock_object(data_motion.DataMotionMixin, 1353 'get_replication_backend_names', 1354 return_value=configured_targets) 1355 complete_failover_call = self.mock_object( 1356 data_motion.DataMotionMixin, '_complete_failover') 1357 1358 self.assertRaises(exception.InvalidReplicationTarget, 1359 self.driver.failover_host, 'fake_context', [], 1360 secondary_id=secondary_id) 1361 self.assertFalse(complete_failover_call.called) 1362 1363 def test_failover_host_unable_to_failover(self): 1364 """This tests executes a method in the DataMotionMixin.""" 1365 self.driver.backend_name = 'dev0' 1366 self.mock_object(data_motion.DataMotionMixin, '_complete_failover', 1367 side_effect=exception.NetAppDriverException) 1368 self.mock_object(data_motion.DataMotionMixin, 1369 'get_replication_backend_names', 1370 return_value=['dev1', 'dev2']) 1371 self.mock_object(self.driver.ssc_library, 'get_ssc_flexvol_names', 1372 return_value=fake_ssc.SSC.keys()) 1373 self.mock_object(self.driver, '_update_zapi_client') 1374 1375 self.assertRaises(exception.UnableToFailOver, 1376 self.driver.failover_host, 'fake_context', [], 1377 secondary_id='dev1') 1378 data_motion.DataMotionMixin._complete_failover.assert_called_once_with( 1379 'dev0', ['dev1', 'dev2'], fake_ssc.SSC.keys(), [], 1380 failover_target='dev1') 1381 self.assertFalse(self.driver._update_zapi_client.called) 1382 1383 def test_failover_host(self): 1384 """This tests executes a method in the DataMotionMixin.""" 1385 self.driver.backend_name = 'dev0' 1386 self.mock_object(data_motion.DataMotionMixin, '_complete_failover', 1387 return_value=('dev1', [])) 1388 self.mock_object(data_motion.DataMotionMixin, 1389 'get_replication_backend_names', 1390 return_value=['dev1', 'dev2']) 1391 self.mock_object(self.driver.ssc_library, 'get_ssc_flexvol_names', 1392 return_value=fake_ssc.SSC.keys()) 1393 self.mock_object(self.driver, '_update_zapi_client') 1394 1395 actual_active, vol_updates, __ = self.driver.failover_host( 1396 'fake_context', [], secondary_id='dev1', groups=[]) 1397 1398 data_motion.DataMotionMixin._complete_failover.assert_called_once_with( 1399 'dev0', ['dev1', 'dev2'], fake_ssc.SSC.keys(), [], 1400 failover_target='dev1') 1401 self.driver._update_zapi_client.assert_called_once_with('dev1') 1402 self.assertTrue(self.driver.failed_over) 1403 self.assertEqual('dev1', self.driver.failed_over_backend_name) 1404 self.assertEqual('dev1', actual_active) 1405 self.assertEqual([], vol_updates) 1406 1407 def test_delete_group_snapshot(self): 1408 mock_delete_backing_file = self.mock_object( 1409 self.driver, '_delete_backing_file_for_snapshot') 1410 snapshots = [fake.VG_SNAPSHOT] 1411 1412 model_update, snapshots_model_update = ( 1413 self.driver.delete_group_snapshot( 1414 fake.VG_CONTEXT, fake.VG_SNAPSHOT, snapshots)) 1415 1416 mock_delete_backing_file.assert_called_once_with(fake.VG_SNAPSHOT) 1417 self.assertIsNone(model_update) 1418 self.assertIsNone(snapshots_model_update) 1419 1420 def test_get_snapshot_backing_flexvol_names(self): 1421 snapshots = [ 1422 {'volume': {'host': 'hostA@192.168.99.25#/fake/volume1'}}, 1423 {'volume': {'host': 'hostA@192.168.1.01#/fake/volume2'}}, 1424 {'volume': {'host': 'hostA@192.168.99.25#/fake/volume3'}}, 1425 {'volume': {'host': 'hostA@192.168.99.25#/fake/volume1'}}, 1426 ] 1427 1428 ssc = { 1429 'volume1': {'pool_name': '/fake/volume1', }, 1430 'volume2': {'pool_name': '/fake/volume2', }, 1431 'volume3': {'pool_name': '/fake/volume3', }, 1432 } 1433 1434 mock_get_ssc = self.mock_object(self.driver.ssc_library, 'get_ssc') 1435 mock_get_ssc.return_value = ssc 1436 1437 hosts = [snap['volume']['host'] for snap in snapshots] 1438 flexvols = self.driver._get_flexvol_names_from_hosts(hosts) 1439 1440 mock_get_ssc.assert_called_once_with() 1441 self.assertEqual(3, len(flexvols)) 1442 self.assertIn('volume1', flexvols) 1443 self.assertIn('volume2', flexvols) 1444 self.assertIn('volume3', flexvols) 1445 1446 def test_get_backing_flexvol_names(self): 1447 mock_ssc_library = self.mock_object( 1448 self.driver.ssc_library, 'get_ssc') 1449 1450 self.driver._get_backing_flexvol_names() 1451 1452 mock_ssc_library.assert_called_once_with() 1453 1454 def test_create_group(self): 1455 1456 model_update = self.driver.create_group( 1457 fake.VG_CONTEXT, fake.VOLUME_GROUP) 1458 1459 self.assertEqual('available', model_update['status']) 1460 1461 def test_update_group(self): 1462 1463 model_update, add_volumes_update, remove_volumes_update = ( 1464 self.driver.update_group(fake.VG_CONTEXT, "foo")) 1465 1466 self.assertIsNone(add_volumes_update) 1467 self.assertIsNone(remove_volumes_update) 1468 1469 @ddt.data(None, 1470 {'replication_status': fields.ReplicationStatus.ENABLED}) 1471 def test_create_group_from_src(self, volume_model_update): 1472 volume_model_update = volume_model_update or {} 1473 volume_model_update.update( 1474 {'provider_location': fake.PROVIDER_LOCATION}) 1475 mock_create_volume_from_snapshot = self.mock_object( 1476 self.driver, 'create_volume_from_snapshot', 1477 return_value=volume_model_update) 1478 1479 model_update, volumes_model_update = ( 1480 self.driver.create_group_from_src( 1481 fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VOLUME], 1482 group_snapshot=fake.VG_SNAPSHOT, 1483 sorted_snapshots=[fake.SNAPSHOT])) 1484 1485 expected_volumes_model_updates = [{'id': fake.VOLUME['id']}] 1486 expected_volumes_model_updates[0].update(volume_model_update) 1487 mock_create_volume_from_snapshot.assert_called_once_with( 1488 fake.VOLUME, fake.SNAPSHOT) 1489 self.assertIsNone(model_update) 1490 self.assertEqual(expected_volumes_model_updates, volumes_model_update) 1491 1492 @ddt.data(None, 1493 {'replication_status': fields.ReplicationStatus.ENABLED}) 1494 def test_create_group_from_src_source_vols(self, volume_model_update): 1495 self.driver.zapi_client = mock.Mock() 1496 mock_get_snapshot_flexvols = self.mock_object( 1497 self.driver, '_get_flexvol_names_from_hosts') 1498 mock_get_snapshot_flexvols.return_value = (set([fake.VG_POOL_NAME])) 1499 mock_clone_backing_file = self.mock_object( 1500 self.driver, '_clone_backing_file_for_volume') 1501 fake_snapshot_name = 'snapshot-temp-' + fake.VOLUME_GROUP['id'] 1502 mock_busy = self.mock_object( 1503 self.driver.zapi_client, 'wait_for_busy_snapshot') 1504 self.mock_object(self.driver, '_get_volume_model_update', 1505 return_value=volume_model_update) 1506 1507 model_update, volumes_model_update = ( 1508 self.driver.create_group_from_src( 1509 fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VG_VOLUME], 1510 source_group=fake.VOLUME_GROUP, 1511 sorted_source_vols=[fake.SOURCE_VG_VOLUME])) 1512 1513 expected_volumes_model_updates = [{ 1514 'id': fake.VG_VOLUME['id'], 1515 'provider_location': fake.PROVIDER_LOCATION, 1516 }] 1517 if volume_model_update: 1518 expected_volumes_model_updates[0].update(volume_model_update) 1519 mock_get_snapshot_flexvols.assert_called_once_with( 1520 [fake.SOURCE_VG_VOLUME['host']]) 1521 self.driver.zapi_client.create_cg_snapshot.assert_called_once_with( 1522 set([fake.VG_POOL_NAME]), fake_snapshot_name) 1523 mock_clone_backing_file.assert_called_once_with( 1524 fake.SOURCE_VG_VOLUME['name'], fake.VG_VOLUME['name'], 1525 fake.SOURCE_VG_VOLUME['id'], source_snapshot=fake_snapshot_name) 1526 mock_busy.assert_called_once_with( 1527 fake.VG_POOL_NAME, fake_snapshot_name) 1528 self.driver.zapi_client.delete_snapshot.assert_called_once_with( 1529 fake.VG_POOL_NAME, fake_snapshot_name) 1530 self.assertIsNone(model_update) 1531 self.assertEqual(expected_volumes_model_updates, volumes_model_update) 1532 1533 def test_create_group_from_src_invalid_parms(self): 1534 model_update, volumes_model_update = ( 1535 self.driver.create_group_from_src( 1536 fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VOLUME])) 1537 1538 self.assertIn('error', model_update['status']) 1539 1540 def test_create_group_snapshot_raise_exception(self): 1541 mock_is_cg_snapshot = self.mock_object( 1542 volume_utils, 'is_group_a_cg_snapshot_type', return_value=True) 1543 mock__get_flexvol_names = self.mock_object( 1544 self.driver, '_get_flexvol_names_from_hosts') 1545 1546 self.mock_object(self.driver.zapi_client, 'create_cg_snapshot', 1547 side_effect=netapp_api.NaApiError) 1548 1549 self.assertRaises(exception.NetAppDriverException, 1550 self.driver.create_group_snapshot, 1551 fake.VG_CONTEXT, 1552 fake.VOLUME_GROUP, 1553 [fake.VG_SNAPSHOT]) 1554 1555 mock_is_cg_snapshot.assert_called_once_with(fake.VOLUME_GROUP) 1556 mock__get_flexvol_names.assert_called_once_with( 1557 [fake.VG_SNAPSHOT['volume']['host']]) 1558 1559 def test_create_group_snapshot(self): 1560 mock_is_cg_snapshot = self.mock_object( 1561 volume_utils, 'is_group_a_cg_snapshot_type', return_value=False) 1562 mock__clone_backing_file_for_volume = self.mock_object( 1563 self.driver, '_clone_backing_file_for_volume') 1564 1565 model_update, snapshots_model_update = ( 1566 self.driver.create_group_snapshot(fake.VG_CONTEXT, 1567 fake.VOLUME_GROUP, 1568 [fake.SNAPSHOT])) 1569 1570 self.assertIsNone(model_update) 1571 self.assertIsNone(snapshots_model_update) 1572 mock_is_cg_snapshot.assert_called_once_with(fake.VOLUME_GROUP) 1573 mock__clone_backing_file_for_volume.assert_called_once_with( 1574 fake.SNAPSHOT['volume_name'], fake.SNAPSHOT['name'], 1575 fake.SNAPSHOT['volume_id'], is_snapshot=True) 1576 1577 def test_create_consistent_group_snapshot(self): 1578 mock_is_cg_snapshot = self.mock_object( 1579 volume_utils, 'is_group_a_cg_snapshot_type', return_value=True) 1580 1581 self.driver.zapi_client = mock.Mock() 1582 mock_get_snapshot_flexvols = self.mock_object( 1583 self.driver, '_get_flexvol_names_from_hosts') 1584 mock_get_snapshot_flexvols.return_value = (set([fake.VG_POOL_NAME])) 1585 mock_clone_backing_file = self.mock_object( 1586 self.driver, '_clone_backing_file_for_volume') 1587 mock_busy = self.mock_object( 1588 self.driver.zapi_client, 'wait_for_busy_snapshot') 1589 1590 model_update, snapshots_model_update = ( 1591 self.driver.create_group_snapshot(fake.VG_CONTEXT, 1592 fake.VOLUME_GROUP, 1593 [fake.VG_SNAPSHOT])) 1594 1595 self.assertIsNone(model_update) 1596 self.assertIsNone(snapshots_model_update) 1597 mock_is_cg_snapshot.assert_called_once_with(fake.VOLUME_GROUP) 1598 mock_get_snapshot_flexvols.assert_called_once_with( 1599 [fake.VG_SNAPSHOT['volume']['host']]) 1600 self.driver.zapi_client.create_cg_snapshot.assert_called_once_with( 1601 set([fake.VG_POOL_NAME]), fake.VOLUME_GROUP_ID) 1602 mock_clone_backing_file.assert_called_once_with( 1603 fake.VG_SNAPSHOT['volume']['name'], fake.VG_SNAPSHOT['name'], 1604 fake.VG_SNAPSHOT['volume']['id'], 1605 source_snapshot=fake.VOLUME_GROUP_ID) 1606 mock_busy.assert_called_once_with( 1607 fake.VG_POOL_NAME, fake.VOLUME_GROUP_ID) 1608 self.driver.zapi_client.delete_snapshot.assert_called_once_with( 1609 fake.VG_POOL_NAME, fake.VOLUME_GROUP_ID) 1610 1611 def test_create_group_snapshot_busy_snapshot(self): 1612 self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type', 1613 return_value=True) 1614 self.driver.zapi_client = mock.Mock() 1615 snapshot = fake.VG_SNAPSHOT 1616 snapshot['volume'] = fake.VG_VOLUME 1617 mock_get_snapshot_flexvols = self.mock_object( 1618 self.driver, '_get_flexvol_names_from_hosts') 1619 mock_get_snapshot_flexvols.return_value = (set([fake.VG_POOL_NAME])) 1620 mock_clone_backing_file = self.mock_object( 1621 self.driver, '_clone_backing_file_for_volume') 1622 mock_busy = self.mock_object( 1623 self.driver.zapi_client, 'wait_for_busy_snapshot') 1624 mock_busy.side_effect = exception.SnapshotIsBusy(snapshot['name']) 1625 mock_mark_snapshot_for_deletion = self.mock_object( 1626 self.driver.zapi_client, 'mark_snapshot_for_deletion') 1627 1628 self.driver.create_group_snapshot( 1629 fake.VG_CONTEXT, fake.VG_SNAPSHOT, [snapshot]) 1630 1631 mock_get_snapshot_flexvols.assert_called_once_with( 1632 [snapshot['volume']['host']]) 1633 self.driver.zapi_client.create_cg_snapshot.assert_called_once_with( 1634 set([fake.VG_POOL_NAME]), fake.VG_SNAPSHOT_ID) 1635 mock_clone_backing_file.assert_called_once_with( 1636 snapshot['volume']['name'], snapshot['name'], 1637 snapshot['volume']['id'], source_snapshot=fake.VG_SNAPSHOT_ID) 1638 mock_busy.assert_called_once_with( 1639 fake.VG_POOL_NAME, fake.VG_SNAPSHOT_ID) 1640 self.driver.zapi_client.delete_snapshot.assert_not_called() 1641 mock_mark_snapshot_for_deletion.assert_called_once_with( 1642 fake.VG_POOL_NAME, fake.VG_SNAPSHOT_ID) 1643 1644 def test_delete_group_volume_delete_failure(self): 1645 self.mock_object(self.driver, '_delete_file', side_effect=Exception) 1646 1647 model_update, volumes = self.driver.delete_group( 1648 fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VG_VOLUME]) 1649 1650 self.assertEqual('deleted', model_update['status']) 1651 self.assertEqual('error_deleting', volumes[0]['status']) 1652 1653 def test_delete_group(self): 1654 mock_delete_file = self.mock_object( 1655 self.driver, '_delete_file') 1656 1657 model_update, volumes = self.driver.delete_group( 1658 fake.VG_CONTEXT, fake.VOLUME_GROUP, [fake.VG_VOLUME]) 1659 1660 self.assertEqual('deleted', model_update['status']) 1661 self.assertEqual('deleted', volumes[0]['status']) 1662 mock_delete_file.assert_called_once_with( 1663 fake.VG_VOLUME_ID, fake.VG_VOLUME_NAME) 1664