"Fossies" - the Fresh Open Source Software Archive

Member "cinder-13.0.7/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py" (4 Oct 2019, 40809 Bytes) of package /linux/misc/openstack/cinder-13.0.7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "test_block_cmode.py": 14.0.2_vs_15.0.0.

    1 # Copyright (c) 2014 Alex Meade.  All rights reserved.
    2 # Copyright (c) 2014 Clinton Knight.  All rights reserved.
    3 # Copyright (c) 2015 Tom Barron.  All rights reserved.
    4 # Copyright (c) 2016 Mike Rooney. All rights reserved.
    5 #
    6 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    7 #    not use this file except in compliance with the License. You may obtain
    8 #    a copy of the License at
    9 #
   10 #         http://www.apache.org/licenses/LICENSE-2.0
   11 #
   12 #    Unless required by applicable law or agreed to in writing, software
   13 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   14 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   15 #    License for the specific language governing permissions and limitations
   16 #    under the License.
   17 """
   18 Mock unit tests for the NetApp block storage C-mode library
   19 """
   20 
   21 import ddt
   22 import mock
   23 
   24 from cinder import exception
   25 from cinder.objects import fields
   26 from cinder import test
   27 import cinder.tests.unit.volume.drivers.netapp.dataontap.fakes as fake
   28 from cinder.tests.unit.volume.drivers.netapp.dataontap.utils import fakes as\
   29     fake_utils
   30 import cinder.tests.unit.volume.drivers.netapp.fakes as na_fakes
   31 from cinder.volume.drivers.netapp.dataontap import block_base
   32 from cinder.volume.drivers.netapp.dataontap import block_cmode
   33 from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
   34 from cinder.volume.drivers.netapp.dataontap.client import client_base
   35 from cinder.volume.drivers.netapp.dataontap.performance import perf_cmode
   36 from cinder.volume.drivers.netapp.dataontap.utils import capabilities
   37 from cinder.volume.drivers.netapp.dataontap.utils import data_motion
   38 from cinder.volume.drivers.netapp.dataontap.utils import loopingcalls
   39 from cinder.volume.drivers.netapp.dataontap.utils import utils as dot_utils
   40 from cinder.volume.drivers.netapp import utils as na_utils
   41 from cinder.volume import utils as volume_utils
   42 
   43 
   44 @ddt.ddt
   45 class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
   46     """Test case for NetApp's C-Mode iSCSI library."""
   47 
   48     def setUp(self):
   49         super(NetAppBlockStorageCmodeLibraryTestCase, self).setUp()
   50 
   51         kwargs = {
   52             'configuration': self.get_config_cmode(),
   53             'host': 'openstack@cdotblock',
   54         }
   55         self.library = block_cmode.NetAppBlockStorageCmodeLibrary(
   56             'driver', 'protocol', **kwargs)
   57 
   58         self.library.zapi_client = mock.Mock()
   59         self.zapi_client = self.library.zapi_client
   60         self.library.perf_library = mock.Mock()
   61         self.library.ssc_library = mock.Mock()
   62         self.library.vserver = mock.Mock()
   63         self.fake_lun = block_base.NetAppLun(fake.LUN_HANDLE, fake.LUN_NAME,
   64                                              fake.SIZE, None)
   65         self.fake_snapshot_lun = block_base.NetAppLun(
   66             fake.SNAPSHOT_LUN_HANDLE, fake.SNAPSHOT_NAME, fake.SIZE, None)
   67         self.mock_object(self.library, 'lun_table')
   68         self.library.lun_table = {
   69             fake.LUN_NAME: self.fake_lun,
   70             fake.SNAPSHOT_NAME: self.fake_snapshot_lun,
   71         }
   72         self.mock_object(block_base.NetAppBlockStorageLibrary, 'delete_volume')
   73 
   74     def get_config_cmode(self):
   75         config = na_fakes.create_configuration_cmode()
   76         config.netapp_storage_protocol = 'iscsi'
   77         config.netapp_login = 'admin'
   78         config.netapp_password = 'pass'
   79         config.netapp_server_hostname = '127.0.0.1'
   80         config.netapp_transport_type = 'https'
   81         config.netapp_server_port = '443'
   82         config.netapp_vserver = 'openstack'
   83         config.netapp_api_trace_pattern = 'fake_regex'
   84         return config
   85 
   86     @mock.patch.object(perf_cmode, 'PerformanceCmodeLibrary', mock.Mock())
   87     @mock.patch.object(client_base.Client, 'get_ontapi_version',
   88                        mock.MagicMock(return_value=(1, 20)))
   89     @mock.patch.object(capabilities.CapabilitiesLibrary,
   90                        'cluster_user_supported')
   91     @mock.patch.object(capabilities.CapabilitiesLibrary,
   92                        'check_api_permissions')
   93     @mock.patch.object(na_utils, 'check_flags')
   94     @mock.patch.object(block_base.NetAppBlockStorageLibrary, 'do_setup')
   95     def test_do_setup(self, super_do_setup, mock_check_flags,
   96                       mock_check_api_permissions, mock_cluster_user_supported):
   97         self.mock_object(client_base.Client, '_init_ssh_client')
   98         self.mock_object(
   99             dot_utils, 'get_backend_configuration',
  100             return_value=self.get_config_cmode())
  101         context = mock.Mock()
  102 
  103         self.library.do_setup(context)
  104 
  105         super_do_setup.assert_called_once_with(context)
  106         self.assertEqual(1, mock_check_flags.call_count)
  107         mock_check_api_permissions.assert_called_once_with()
  108         mock_cluster_user_supported.assert_called_once_with()
  109 
  110     def test_check_for_setup_error(self):
  111         super_check_for_setup_error = self.mock_object(
  112             block_base.NetAppBlockStorageLibrary, 'check_for_setup_error')
  113         mock_get_pool_map = self.mock_object(
  114             self.library, '_get_flexvol_to_pool_map',
  115             return_value={'fake_map': None})
  116         mock_add_looping_tasks = self.mock_object(
  117             self.library, '_add_looping_tasks')
  118 
  119         self.library.check_for_setup_error()
  120 
  121         self.assertEqual(1, super_check_for_setup_error.call_count)
  122         self.assertEqual(1, mock_add_looping_tasks.call_count)
  123         mock_get_pool_map.assert_called_once_with()
  124         mock_add_looping_tasks.assert_called_once_with()
  125 
  126     def test_check_for_setup_error_no_filtered_pools(self):
  127         self.mock_object(block_base.NetAppBlockStorageLibrary,
  128                          'check_for_setup_error')
  129         self.mock_object(self.library, '_add_looping_tasks')
  130         self.mock_object(
  131             self.library, '_get_flexvol_to_pool_map', return_value={})
  132 
  133         self.assertRaises(exception.NetAppDriverException,
  134                           self.library.check_for_setup_error)
  135 
  136     @ddt.data({'replication_enabled': True, 'failed_over': False,
  137                'cluster_credentials': True},
  138               {'replication_enabled': True, 'failed_over': True,
  139                'cluster_credentials': True},
  140               {'replication_enabled': False, 'failed_over': False,
  141                'cluster_credentials': False})
  142     @ddt.unpack
  143     def test_handle_housekeeping_tasks(
  144             self, replication_enabled, failed_over, cluster_credentials):
  145         self.library.using_cluster_credentials = cluster_credentials
  146         ensure_mirrors = self.mock_object(data_motion.DataMotionMixin,
  147                                           'ensure_snapmirrors')
  148         self.mock_object(self.library.ssc_library, 'get_ssc_flexvol_names',
  149                          return_value=fake_utils.SSC.keys())
  150         mock_remove_unused_qos_policy_groups = self.mock_object(
  151             self.zapi_client, 'remove_unused_qos_policy_groups')
  152         self.library.replication_enabled = replication_enabled
  153         self.library.failed_over = failed_over
  154 
  155         self.library._handle_housekeeping_tasks()
  156 
  157         if self.library.using_cluster_credentials:
  158             mock_remove_unused_qos_policy_groups.assert_called_once_with()
  159         else:
  160             mock_remove_unused_qos_policy_groups.assert_not_called()
  161 
  162         if replication_enabled and not failed_over:
  163             ensure_mirrors.assert_called_once_with(
  164                 self.library.configuration, self.library.backend_name,
  165                 fake_utils.SSC.keys())
  166         else:
  167             self.assertFalse(ensure_mirrors.called)
  168 
  169     def test_handle_ems_logging(self):
  170         volume_list = ['vol0', 'vol1', 'vol2']
  171         self.mock_object(
  172             self.library.ssc_library, 'get_ssc_flexvol_names',
  173             return_value=volume_list)
  174         self.mock_object(
  175             dot_utils, 'build_ems_log_message_0',
  176             return_value='fake_base_ems_log_message')
  177         self.mock_object(
  178             dot_utils, 'build_ems_log_message_1',
  179             return_value='fake_pool_ems_log_message')
  180         mock_send_ems_log_message = self.mock_object(
  181             self.zapi_client, 'send_ems_log_message')
  182 
  183         self.library._handle_ems_logging()
  184 
  185         mock_send_ems_log_message.assert_has_calls([
  186             mock.call('fake_base_ems_log_message'),
  187             mock.call('fake_pool_ems_log_message'),
  188         ])
  189         dot_utils.build_ems_log_message_0.assert_called_once_with(
  190             self.library.driver_name, self.library.app_version)
  191         dot_utils.build_ems_log_message_1.assert_called_once_with(
  192             self.library.driver_name, self.library.app_version,
  193             self.library.vserver, volume_list, [])
  194 
  195     def test_find_mapped_lun_igroup(self):
  196         igroups = [fake.IGROUP1]
  197         self.zapi_client.get_igroup_by_initiators.return_value = igroups
  198 
  199         lun_maps = [{'initiator-group': fake.IGROUP1_NAME,
  200                      'lun-id': '1',
  201                      'vserver': fake.VSERVER_NAME}]
  202         self.zapi_client.get_lun_map.return_value = lun_maps
  203 
  204         (igroup, lun_id) = self.library._find_mapped_lun_igroup(
  205             fake.LUN_PATH, fake.FC_FORMATTED_INITIATORS)
  206 
  207         self.assertEqual(fake.IGROUP1_NAME, igroup)
  208         self.assertEqual('1', lun_id)
  209 
  210     def test_find_mapped_lun_igroup_initiator_mismatch(self):
  211         self.zapi_client.get_igroup_by_initiators.return_value = []
  212 
  213         lun_maps = [{'initiator-group': fake.IGROUP1_NAME,
  214                      'lun-id': '1',
  215                      'vserver': fake.VSERVER_NAME}]
  216         self.zapi_client.get_lun_map.return_value = lun_maps
  217 
  218         (igroup, lun_id) = self.library._find_mapped_lun_igroup(
  219             fake.LUN_PATH, fake.FC_FORMATTED_INITIATORS)
  220 
  221         self.assertIsNone(igroup)
  222         self.assertIsNone(lun_id)
  223 
  224     def test_find_mapped_lun_igroup_name_mismatch(self):
  225         igroups = [{'initiator-group-os-type': 'linux',
  226                     'initiator-group-type': 'fcp',
  227                     'initiator-group-name': 'igroup2'}]
  228         self.zapi_client.get_igroup_by_initiators.return_value = igroups
  229 
  230         lun_maps = [{'initiator-group': fake.IGROUP1_NAME,
  231                      'lun-id': '1',
  232                      'vserver': fake.VSERVER_NAME}]
  233         self.zapi_client.get_lun_map.return_value = lun_maps
  234 
  235         (igroup, lun_id) = self.library._find_mapped_lun_igroup(
  236             fake.LUN_PATH, fake.FC_FORMATTED_INITIATORS)
  237 
  238         self.assertIsNone(igroup)
  239         self.assertIsNone(lun_id)
  240 
  241     def test_find_mapped_lun_igroup_no_igroup_prefix(self):
  242         igroups = [{'initiator-group-os-type': 'linux',
  243                     'initiator-group-type': 'fcp',
  244                     'initiator-group-name': 'igroup2'}]
  245         self.zapi_client.get_igroup_by_initiators.return_value = igroups
  246 
  247         lun_maps = [{'initiator-group': 'igroup2',
  248                      'lun-id': '1',
  249                      'vserver': fake.VSERVER_NAME}]
  250         self.zapi_client.get_lun_map.return_value = lun_maps
  251 
  252         (igroup, lun_id) = self.library._find_mapped_lun_igroup(
  253             fake.LUN_PATH, fake.FC_FORMATTED_INITIATORS)
  254 
  255         self.assertIsNone(igroup)
  256         self.assertIsNone(lun_id)
  257 
  258     def test_clone_lun_zero_block_count(self):
  259         """Test for when clone lun is not passed a block count."""
  260 
  261         self.library._get_lun_attr = mock.Mock(return_value={'Volume':
  262                                                              'fakeLUN'})
  263         self.library.zapi_client = mock.Mock()
  264         self.library.zapi_client.get_lun_by_args.return_value = [
  265             mock.Mock(spec=netapp_api.NaElement)]
  266         lun = fake.FAKE_LUN
  267         self.library._get_lun_by_args = mock.Mock(return_value=[lun])
  268         self.library._add_lun_to_table = mock.Mock()
  269 
  270         self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false')
  271 
  272         self.library.zapi_client.clone_lun.assert_called_once_with(
  273             'fakeLUN', 'fakeLUN', 'newFakeLUN', 'false', block_count=0,
  274             dest_block=0, src_block=0, qos_policy_group_name=None,
  275             source_snapshot=None, is_snapshot=False)
  276 
  277     def test_clone_lun_blocks(self):
  278         """Test for when clone lun is passed block information."""
  279         block_count = 10
  280         src_block = 10
  281         dest_block = 30
  282 
  283         self.library._get_lun_attr = mock.Mock(return_value={'Volume':
  284                                                              'fakeLUN'})
  285         self.library.zapi_client = mock.Mock()
  286         self.library.zapi_client.get_lun_by_args.return_value = [
  287             mock.Mock(spec=netapp_api.NaElement)]
  288         lun = fake.FAKE_LUN
  289         self.library._get_lun_by_args = mock.Mock(return_value=[lun])
  290         self.library._add_lun_to_table = mock.Mock()
  291 
  292         self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false',
  293                                 block_count=block_count, src_block=src_block,
  294                                 dest_block=dest_block)
  295 
  296         self.library.zapi_client.clone_lun.assert_called_once_with(
  297             'fakeLUN', 'fakeLUN', 'newFakeLUN', 'false',
  298             block_count=block_count, dest_block=dest_block,
  299             src_block=src_block, qos_policy_group_name=None,
  300             source_snapshot=None, is_snapshot=False)
  301 
  302     def test_clone_lun_no_space_reservation(self):
  303         """Test for when space_reservation is not passed."""
  304 
  305         self.library._get_lun_attr = mock.Mock(return_value={'Volume':
  306                                                              'fakeLUN'})
  307         self.library.zapi_client = mock.Mock()
  308         self.library.lun_space_reservation = 'false'
  309         self.library.zapi_client.get_lun_by_args.return_value = [
  310             mock.Mock(spec=netapp_api.NaElement)]
  311         lun = fake.FAKE_LUN
  312         self.library._get_lun_by_args = mock.Mock(return_value=[lun])
  313         self.library._add_lun_to_table = mock.Mock()
  314 
  315         self.library._clone_lun('fakeLUN', 'newFakeLUN', is_snapshot=True)
  316 
  317         self.library.zapi_client.clone_lun.assert_called_once_with(
  318             'fakeLUN', 'fakeLUN', 'newFakeLUN', 'false', block_count=0,
  319             dest_block=0, src_block=0, qos_policy_group_name=None,
  320             source_snapshot=None, is_snapshot=True)
  321 
  322     def test_get_fc_target_wwpns(self):
  323         ports = [fake.FC_FORMATTED_TARGET_WWPNS[0],
  324                  fake.FC_FORMATTED_TARGET_WWPNS[1]]
  325         self.zapi_client.get_fc_target_wwpns.return_value = ports
  326 
  327         result = self.library._get_fc_target_wwpns()
  328 
  329         self.assertSetEqual(set(ports), set(result))
  330 
  331     def test_create_lun(self):
  332         self.library._create_lun(
  333             fake.VOLUME_ID, fake.LUN_ID, fake.LUN_SIZE, fake.LUN_METADATA)
  334 
  335         self.library.zapi_client.create_lun.assert_called_once_with(
  336             fake.VOLUME_ID, fake.LUN_ID, fake.LUN_SIZE, fake.LUN_METADATA,
  337             None)
  338 
  339     @ddt.data({'replication_backends': [], 'cluster_credentials': False},
  340               {'replication_backends': ['target_1', 'target_2'],
  341                'cluster_credentials': True})
  342     @ddt.unpack
  343     def test_get_pool_stats(self, replication_backends, cluster_credentials):
  344         self.library.using_cluster_credentials = cluster_credentials
  345         ssc = {
  346             'vola': {
  347                 'pool_name': 'vola',
  348                 'thick_provisioning_support': True,
  349                 'thin_provisioning_support': False,
  350                 'netapp_thin_provisioned': 'false',
  351                 'netapp_compression': 'false',
  352                 'netapp_mirrored': 'false',
  353                 'netapp_dedup': 'true',
  354                 'netapp_aggregate': 'aggr1',
  355                 'netapp_raid_type': 'raid_dp',
  356                 'netapp_disk_type': 'SSD',
  357             },
  358         }
  359         mock_get_ssc = self.mock_object(self.library.ssc_library,
  360                                         'get_ssc',
  361                                         return_value=ssc)
  362         mock_get_aggrs = self.mock_object(self.library.ssc_library,
  363                                           'get_ssc_aggregates',
  364                                           return_value=['aggr1'])
  365         self.mock_object(self.library, 'get_replication_backend_names',
  366                          return_value=replication_backends)
  367 
  368         self.library.reserved_percentage = 5
  369         self.library.max_over_subscription_ratio = 10
  370         self.library.perf_library.get_node_utilization_for_pool = (
  371             mock.Mock(return_value=30.0))
  372         mock_capacities = {
  373             'size-total': 10737418240.0,
  374             'size-available': 2147483648.0,
  375         }
  376         self.mock_object(self.zapi_client,
  377                          'get_flexvol_capacity',
  378                          return_value=mock_capacities)
  379         self.mock_object(self.zapi_client,
  380                          'get_flexvol_dedupe_used_percent',
  381                          return_value=55.0)
  382 
  383         aggr_capacities = {
  384             'aggr1': {
  385                 'percent-used': 45,
  386                 'size-available': 59055800320.0,
  387                 'size-total': 107374182400.0,
  388             },
  389         }
  390         mock_get_aggr_capacities = self.mock_object(
  391             self.zapi_client, 'get_aggregate_capacities',
  392             return_value=aggr_capacities)
  393 
  394         result = self.library._get_pool_stats(filter_function='filter',
  395                                               goodness_function='goodness')
  396 
  397         expected = [{
  398             'pool_name': 'vola',
  399             'QoS_support': True,
  400             'consistencygroup_support': True,
  401             'consistent_group_snapshot_enabled': True,
  402             'reserved_percentage': 5,
  403             'max_over_subscription_ratio': 10.0,
  404             'multiattach': True,
  405             'total_capacity_gb': 10.0,
  406             'free_capacity_gb': 2.0,
  407             'netapp_dedupe_used_percent': 55.0,
  408             'netapp_aggregate_used_percent': 45,
  409             'utilization': 30.0,
  410             'filter_function': 'filter',
  411             'goodness_function': 'goodness',
  412             'thick_provisioning_support': True,
  413             'thin_provisioning_support': False,
  414             'netapp_thin_provisioned': 'false',
  415             'netapp_compression': 'false',
  416             'netapp_mirrored': 'false',
  417             'netapp_dedup': 'true',
  418             'netapp_aggregate': 'aggr1',
  419             'netapp_raid_type': 'raid_dp',
  420             'netapp_disk_type': 'SSD',
  421             'replication_enabled': False,
  422             'online_extend_support': False,
  423         }]
  424 
  425         expected[0].update({'QoS_support': cluster_credentials})
  426         if not cluster_credentials:
  427             expected[0].update({
  428                 'netapp_aggregate_used_percent': 0,
  429                 'netapp_dedupe_used_percent': 0
  430             })
  431 
  432         if replication_backends:
  433             expected[0].update({
  434                 'replication_enabled': True,
  435                 'replication_count': len(replication_backends),
  436                 'replication_targets': replication_backends,
  437                 'replication_type': 'async',
  438             })
  439 
  440         self.assertEqual(expected, result)
  441         mock_get_ssc.assert_called_once_with()
  442         if cluster_credentials:
  443             mock_get_aggrs.assert_called_once_with()
  444             mock_get_aggr_capacities.assert_called_once_with(['aggr1'])
  445 
  446     @ddt.data({}, None)
  447     def test_get_pool_stats_no_ssc_vols(self, ssc):
  448 
  449         mock_get_ssc = self.mock_object(self.library.ssc_library,
  450                                         'get_ssc',
  451                                         return_value=ssc)
  452 
  453         pools = self.library._get_pool_stats()
  454 
  455         self.assertListEqual([], pools)
  456         mock_get_ssc.assert_called_once_with()
  457 
  458     @ddt.data(r'open+|demix+', 'open.+', r'.+\d', '^((?!mix+).)*$',
  459               'open123, open321')
  460     def test_get_pool_map_match_selected_pools(self, patterns):
  461 
  462         self.library.configuration.netapp_pool_name_search_pattern = patterns
  463         mock_list_flexvols = self.mock_object(
  464             self.zapi_client, 'list_flexvols',
  465             return_value=fake.FAKE_CMODE_VOLUMES)
  466 
  467         result = self.library._get_flexvol_to_pool_map()
  468 
  469         expected = {
  470             'open123': {
  471                 'pool_name': 'open123',
  472             },
  473             'open321': {
  474                 'pool_name': 'open321',
  475             },
  476         }
  477         self.assertEqual(expected, result)
  478         mock_list_flexvols.assert_called_once_with()
  479 
  480     @ddt.data('', 'mix.+|open.+', '.+', 'open123, mixed, open321',
  481               '.*?')
  482     def test_get_pool_map_match_all_pools(self, patterns):
  483 
  484         self.library.configuration.netapp_pool_name_search_pattern = patterns
  485         mock_list_flexvols = self.mock_object(
  486             self.zapi_client, 'list_flexvols',
  487             return_value=fake.FAKE_CMODE_VOLUMES)
  488 
  489         result = self.library._get_flexvol_to_pool_map()
  490 
  491         self.assertEqual(fake.FAKE_CMODE_POOL_MAP, result)
  492         mock_list_flexvols.assert_called_once_with()
  493 
  494     def test_get_pool_map_invalid_conf(self):
  495         """Verify an exception is raised if the regex pattern is invalid"""
  496         self.library.configuration.netapp_pool_name_search_pattern = '(.+'
  497 
  498         self.assertRaises(exception.InvalidConfigurationValue,
  499                           self.library._get_flexvol_to_pool_map)
  500 
  501     @ddt.data('abc|stackopen|openstack|abc*', 'abc', 'stackopen', 'openstack',
  502               'abc*', '^$')
  503     def test_get_pool_map_non_matching_patterns(self, patterns):
  504 
  505         self.library.configuration.netapp_pool_name_search_pattern = patterns
  506         mock_list_flexvols = self.mock_object(
  507             self.zapi_client, 'list_flexvols',
  508             return_value=fake.FAKE_CMODE_VOLUMES)
  509 
  510         result = self.library._get_flexvol_to_pool_map()
  511 
  512         self.assertEqual({}, result)
  513         mock_list_flexvols.assert_called_once_with()
  514 
  515     def test_update_ssc(self):
  516 
  517         mock_get_pool_map = self.mock_object(
  518             self.library, '_get_flexvol_to_pool_map',
  519             return_value=fake.FAKE_CMODE_VOLUMES)
  520 
  521         result = self.library._update_ssc()
  522 
  523         self.assertIsNone(result)
  524         mock_get_pool_map.assert_called_once_with()
  525         self.library.ssc_library.update_ssc.assert_called_once_with(
  526             fake.FAKE_CMODE_VOLUMES)
  527 
  528     def test_delete_volume(self):
  529         self.mock_object(na_utils, 'get_valid_qos_policy_group_info',
  530                          return_value=fake.QOS_POLICY_GROUP_INFO)
  531         self.mock_object(self.library, '_mark_qos_policy_group_for_deletion')
  532 
  533         self.library.delete_volume(fake.VOLUME)
  534 
  535         (block_base.NetAppBlockStorageLibrary.delete_volume.
  536             assert_called_once_with(fake.VOLUME))
  537         na_utils.get_valid_qos_policy_group_info.assert_called_once_with(
  538             fake.VOLUME)
  539         (self.library._mark_qos_policy_group_for_deletion.
  540             assert_called_once_with(fake.QOS_POLICY_GROUP_INFO))
  541 
  542     def test_delete_volume_get_valid_qos_policy_group_info_exception(self):
  543         self.mock_object(na_utils, 'get_valid_qos_policy_group_info',
  544                          side_effect=exception.Invalid)
  545         self.mock_object(self.library, '_mark_qos_policy_group_for_deletion')
  546 
  547         self.library.delete_volume(fake.VOLUME)
  548 
  549         (block_base.NetAppBlockStorageLibrary.delete_volume.
  550             assert_called_once_with(fake.VOLUME))
  551         (self.library._mark_qos_policy_group_for_deletion.
  552             assert_called_once_with(None))
  553 
  554     def test_setup_qos_for_volume(self):
  555         self.mock_object(na_utils, 'get_valid_qos_policy_group_info',
  556                          return_value=fake.QOS_POLICY_GROUP_INFO)
  557         self.mock_object(self.zapi_client, 'provision_qos_policy_group')
  558 
  559         result = self.library._setup_qos_for_volume(fake.VOLUME,
  560                                                     fake.EXTRA_SPECS)
  561 
  562         self.assertEqual(fake.QOS_POLICY_GROUP_INFO, result)
  563         self.zapi_client.provision_qos_policy_group.\
  564             assert_called_once_with(fake.QOS_POLICY_GROUP_INFO)
  565 
  566     def test_setup_qos_for_volume_exception_path(self):
  567         self.mock_object(na_utils, 'get_valid_qos_policy_group_info',
  568                          side_effect=exception.Invalid)
  569         self.mock_object(self.zapi_client, 'provision_qos_policy_group')
  570 
  571         self.assertRaises(exception.VolumeBackendAPIException,
  572                           self.library._setup_qos_for_volume, fake.VOLUME,
  573                           fake.EXTRA_SPECS)
  574 
  575         self.assertEqual(0,
  576                          self.zapi_client.
  577                          provision_qos_policy_group.call_count)
  578 
  579     def test_mark_qos_policy_group_for_deletion(self):
  580         self.mock_object(self.zapi_client,
  581                          'mark_qos_policy_group_for_deletion')
  582 
  583         self.library._mark_qos_policy_group_for_deletion(
  584             fake.QOS_POLICY_GROUP_INFO)
  585 
  586         self.zapi_client.mark_qos_policy_group_for_deletion\
  587             .assert_called_once_with(fake.QOS_POLICY_GROUP_INFO)
  588 
  589     def test_unmanage(self):
  590         self.mock_object(na_utils, 'get_valid_qos_policy_group_info',
  591                          return_value=fake.QOS_POLICY_GROUP_INFO)
  592         self.mock_object(self.library, '_mark_qos_policy_group_for_deletion')
  593         self.mock_object(block_base.NetAppBlockStorageLibrary, 'unmanage')
  594 
  595         self.library.unmanage(fake.VOLUME)
  596 
  597         na_utils.get_valid_qos_policy_group_info.assert_called_once_with(
  598             fake.VOLUME)
  599         self.library._mark_qos_policy_group_for_deletion\
  600             .assert_called_once_with(fake.QOS_POLICY_GROUP_INFO)
  601         block_base.NetAppBlockStorageLibrary.unmanage.assert_called_once_with(
  602             fake.VOLUME)
  603 
  604     def test_unmanage_w_invalid_qos_policy(self):
  605         self.mock_object(na_utils, 'get_valid_qos_policy_group_info',
  606                          side_effect=exception.Invalid)
  607         self.mock_object(self.library, '_mark_qos_policy_group_for_deletion')
  608         self.mock_object(block_base.NetAppBlockStorageLibrary, 'unmanage')
  609 
  610         self.library.unmanage(fake.VOLUME)
  611 
  612         na_utils.get_valid_qos_policy_group_info.assert_called_once_with(
  613             fake.VOLUME)
  614         self.library._mark_qos_policy_group_for_deletion\
  615             .assert_called_once_with(None)
  616         block_base.NetAppBlockStorageLibrary.unmanage.assert_called_once_with(
  617             fake.VOLUME)
  618 
  619     def test_manage_existing_lun_same_name(self):
  620         mock_lun = block_base.NetAppLun('handle', 'name', '1',
  621                                         {'Path': '/vol/FAKE_CMODE_VOL1/name'})
  622         self.library._get_existing_vol_with_manage_ref = mock.Mock(
  623             return_value=mock_lun)
  624         self.mock_object(na_utils, 'get_volume_extra_specs')
  625         self.mock_object(na_utils, 'log_extra_spec_warnings')
  626         self.library._check_volume_type_for_lun = mock.Mock()
  627         self.library._setup_qos_for_volume = mock.Mock()
  628         self.mock_object(na_utils, 'get_qos_policy_group_name_from_info',
  629                          return_value=fake.QOS_POLICY_GROUP_NAME)
  630         self.library._add_lun_to_table = mock.Mock()
  631         self.zapi_client.move_lun = mock.Mock()
  632         mock_set_lun_qos_policy_group = self.mock_object(
  633             self.zapi_client, 'set_lun_qos_policy_group')
  634 
  635         self.library.manage_existing({'name': 'name'}, {'ref': 'ref'})
  636 
  637         self.library._get_existing_vol_with_manage_ref.assert_called_once_with(
  638             {'ref': 'ref'})
  639         self.assertEqual(1, self.library._check_volume_type_for_lun.call_count)
  640         self.assertEqual(1, self.library._add_lun_to_table.call_count)
  641         self.assertEqual(0, self.zapi_client.move_lun.call_count)
  642         self.assertEqual(1, mock_set_lun_qos_policy_group.call_count)
  643 
  644     def test_manage_existing_lun_new_path(self):
  645         mock_lun = block_base.NetAppLun(
  646             'handle', 'name', '1', {'Path': '/vol/FAKE_CMODE_VOL1/name'})
  647         self.library._get_existing_vol_with_manage_ref = mock.Mock(
  648             return_value=mock_lun)
  649         self.mock_object(na_utils, 'get_volume_extra_specs')
  650         self.mock_object(na_utils, 'log_extra_spec_warnings')
  651         self.library._check_volume_type_for_lun = mock.Mock()
  652         self.library._add_lun_to_table = mock.Mock()
  653         self.zapi_client.move_lun = mock.Mock()
  654 
  655         self.library.manage_existing({'name': 'volume'}, {'ref': 'ref'})
  656 
  657         self.assertEqual(
  658             2, self.library._get_existing_vol_with_manage_ref.call_count)
  659         self.assertEqual(1, self.library._check_volume_type_for_lun.call_count)
  660         self.assertEqual(1, self.library._add_lun_to_table.call_count)
  661         self.zapi_client.move_lun.assert_called_once_with(
  662             '/vol/FAKE_CMODE_VOL1/name', '/vol/FAKE_CMODE_VOL1/volume')
  663 
  664     @ddt.data({'secondary_id': 'dev0', 'configured_targets': ['dev1']},
  665               {'secondary_id': 'dev3', 'configured_targets': ['dev1', 'dev2']},
  666               {'secondary_id': 'dev1', 'configured_targets': []},
  667               {'secondary_id': None, 'configured_targets': []})
  668     @ddt.unpack
  669     def test_failover_host_invalid_replication_target(self, secondary_id,
  670                                                       configured_targets):
  671         """This tests executes a method in the DataMotionMixin."""
  672         self.library.backend_name = 'dev0'
  673         self.mock_object(data_motion.DataMotionMixin,
  674                          'get_replication_backend_names',
  675                          return_value=configured_targets)
  676         complete_failover_call = self.mock_object(
  677             data_motion.DataMotionMixin, '_complete_failover')
  678 
  679         self.assertRaises(exception.InvalidReplicationTarget,
  680                           self.library.failover_host, 'fake_context', [],
  681                           secondary_id=secondary_id)
  682         self.assertFalse(complete_failover_call.called)
  683 
  684     def test_failover_host_unable_to_failover(self):
  685         """This tests executes a method in the DataMotionMixin."""
  686         self.library.backend_name = 'dev0'
  687         self.mock_object(
  688             data_motion.DataMotionMixin, '_complete_failover',
  689             side_effect=exception.NetAppDriverException)
  690         self.mock_object(data_motion.DataMotionMixin,
  691                          'get_replication_backend_names',
  692                          return_value=['dev1', 'dev2'])
  693         self.mock_object(self.library.ssc_library, 'get_ssc_flexvol_names',
  694                          return_value=fake_utils.SSC.keys())
  695         self.mock_object(self.library, '_update_zapi_client')
  696 
  697         self.assertRaises(exception.UnableToFailOver,
  698                           self.library.failover_host, 'fake_context', [],
  699                           secondary_id='dev1')
  700         data_motion.DataMotionMixin._complete_failover.assert_called_once_with(
  701             'dev0', ['dev1', 'dev2'], fake_utils.SSC.keys(), [],
  702             failover_target='dev1')
  703         self.assertFalse(self.library._update_zapi_client.called)
  704 
  705     def test_failover_host(self):
  706         """This tests executes a method in the DataMotionMixin."""
  707         self.library.backend_name = 'dev0'
  708         self.mock_object(data_motion.DataMotionMixin, '_complete_failover',
  709                          return_value=('dev1', []))
  710         self.mock_object(data_motion.DataMotionMixin,
  711                          'get_replication_backend_names',
  712                          return_value=['dev1', 'dev2'])
  713         self.mock_object(self.library.ssc_library, 'get_ssc_flexvol_names',
  714                          return_value=fake_utils.SSC.keys())
  715         self.mock_object(self.library, '_update_zapi_client')
  716 
  717         actual_active, vol_updates, __ = self.library.failover_host(
  718             'fake_context', [], secondary_id='dev1', groups=[])
  719 
  720         data_motion.DataMotionMixin._complete_failover.assert_called_once_with(
  721             'dev0', ['dev1', 'dev2'], fake_utils.SSC.keys(), [],
  722             failover_target='dev1')
  723         self.library._update_zapi_client.assert_called_once_with('dev1')
  724         self.assertTrue(self.library.failed_over)
  725         self.assertEqual('dev1', self.library.failed_over_backend_name)
  726         self.assertEqual('dev1', actual_active)
  727         self.assertEqual([], vol_updates)
  728 
  729     def test_add_looping_tasks(self):
  730         mock_update_ssc = self.mock_object(self.library, '_update_ssc')
  731         mock_handle_housekeeping = self.mock_object(
  732             self.library, '_handle_housekeeping_tasks')
  733         mock_add_task = self.mock_object(self.library.loopingcalls, 'add_task')
  734         mock_super_add_looping_tasks = self.mock_object(
  735             block_base.NetAppBlockStorageLibrary, '_add_looping_tasks')
  736 
  737         self.library._add_looping_tasks()
  738 
  739         mock_update_ssc.assert_called_once_with()
  740         mock_add_task.assert_has_calls([
  741             mock.call(mock_update_ssc,
  742                       loopingcalls.ONE_HOUR,
  743                       loopingcalls.ONE_HOUR),
  744             mock.call(mock_handle_housekeeping,
  745                       loopingcalls.TEN_MINUTES,
  746                       0)])
  747         mock_super_add_looping_tasks.assert_called_once_with()
  748 
  749     def test_get_backing_flexvol_names(self):
  750         mock_ssc_library = self.mock_object(
  751             self.library.ssc_library, 'get_ssc')
  752 
  753         self.library._get_backing_flexvol_names()
  754 
  755         mock_ssc_library.assert_called_once_with()
  756 
  757     def test_create_group(self):
  758 
  759         model_update = self.library.create_group(
  760             fake.VOLUME_GROUP)
  761 
  762         self.assertEqual('available', model_update['status'])
  763 
  764     def test_delete_group_volume_delete_failure(self):
  765         self.mock_object(block_cmode, 'LOG')
  766         self.mock_object(self.library, '_delete_lun', side_effect=Exception)
  767 
  768         model_update, volumes = self.library.delete_group(
  769             fake.VOLUME_GROUP, [fake.VG_VOLUME])
  770 
  771         self.assertEqual('deleted', model_update['status'])
  772         self.assertEqual('error_deleting', volumes[0]['status'])
  773         self.assertEqual(1, block_cmode.LOG.exception.call_count)
  774 
  775     def test_update_group(self):
  776 
  777         model_update, add_volumes_update, remove_volumes_update = (
  778             self.library.update_group(fake.VOLUME_GROUP))
  779 
  780         self.assertIsNone(model_update)
  781         self.assertIsNone(add_volumes_update)
  782         self.assertIsNone(remove_volumes_update)
  783 
  784     def test_delete_group_not_found(self):
  785         self.mock_object(block_cmode, 'LOG')
  786         self.mock_object(self.library, '_get_lun_attr', return_value=None)
  787 
  788         model_update, volumes = self.library.delete_group(
  789             fake.VOLUME_GROUP, [fake.VG_VOLUME])
  790 
  791         self.assertEqual(0, block_cmode.LOG.error.call_count)
  792         self.assertEqual(0, block_cmode.LOG.info.call_count)
  793 
  794         self.assertEqual('deleted', model_update['status'])
  795         self.assertEqual('deleted', volumes[0]['status'])
  796 
  797     def test_create_group_snapshot_raise_exception(self):
  798         self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
  799                          return_value=True)
  800 
  801         mock_extract_host = self.mock_object(
  802             volume_utils, 'extract_host', return_value=fake.POOL_NAME)
  803 
  804         self.mock_object(self.zapi_client, 'create_cg_snapshot',
  805                          side_effect=netapp_api.NaApiError)
  806 
  807         self.assertRaises(exception.NetAppDriverException,
  808                           self.library.create_group_snapshot,
  809                           fake.VOLUME_GROUP,
  810                           [fake.VG_SNAPSHOT])
  811 
  812         mock_extract_host.assert_called_once_with(
  813             fake.VG_SNAPSHOT['volume']['host'], level='pool')
  814 
  815     def test_create_group_snapshot(self):
  816         self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
  817                          return_value=False)
  818 
  819         fake_lun = block_base.NetAppLun(fake.LUN_HANDLE, fake.LUN_ID,
  820                                         fake.LUN_SIZE, fake.LUN_METADATA)
  821         self.mock_object(self.library, '_get_lun_from_table',
  822                          return_value=fake_lun)
  823         mock__clone_lun = self.mock_object(self.library, '_clone_lun')
  824 
  825         model_update, snapshots_model_update = (
  826             self.library.create_group_snapshot(fake.VOLUME_GROUP,
  827                                                [fake.SNAPSHOT]))
  828 
  829         self.assertIsNone(model_update)
  830         self.assertIsNone(snapshots_model_update)
  831         mock__clone_lun.assert_called_once_with(fake_lun.name,
  832                                                 fake.SNAPSHOT['name'],
  833                                                 space_reserved='false',
  834                                                 is_snapshot=True)
  835 
  836     def test_create_consistent_group_snapshot(self):
  837         self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
  838                          return_value=True)
  839 
  840         self.mock_object(volume_utils, 'extract_host',
  841                          return_value=fake.POOL_NAME)
  842         mock_create_cg_snapshot = self.mock_object(
  843             self.zapi_client, 'create_cg_snapshot')
  844         mock__clone_lun = self.mock_object(self.library, '_clone_lun')
  845         mock_wait_for_busy_snapshot = self.mock_object(
  846             self.zapi_client, 'wait_for_busy_snapshot')
  847         mock_delete_snapshot = self.mock_object(
  848             self.zapi_client, 'delete_snapshot')
  849 
  850         model_update, snapshots_model_update = (
  851             self.library.create_group_snapshot(fake.VOLUME_GROUP,
  852                                                [fake.VG_SNAPSHOT]))
  853 
  854         self.assertIsNone(model_update)
  855         self.assertIsNone(snapshots_model_update)
  856 
  857         mock_create_cg_snapshot.assert_called_once_with(
  858             set([fake.POOL_NAME]), fake.VOLUME_GROUP['id'])
  859         mock__clone_lun.assert_called_once_with(
  860             fake.VG_SNAPSHOT['volume']['name'],
  861             fake.VG_SNAPSHOT['name'],
  862             source_snapshot=fake.VOLUME_GROUP['id'])
  863         mock_wait_for_busy_snapshot.assert_called_once_with(
  864             fake.POOL_NAME, fake.VOLUME_GROUP['id'])
  865         mock_delete_snapshot.assert_called_once_with(
  866             fake.POOL_NAME, fake.VOLUME_GROUP['id'])
  867 
  868     @ddt.data(None,
  869               {'replication_status': fields.ReplicationStatus.ENABLED})
  870     def test_create_group_from_src_snapshot(self, volume_model_update):
  871         mock_clone_source_to_destination = self.mock_object(
  872             self.library, '_clone_source_to_destination',
  873             return_value=volume_model_update)
  874 
  875         actual_return_value = self.library.create_group_from_src(
  876             fake.VOLUME_GROUP, [fake.VOLUME], group_snapshot=fake.VG_SNAPSHOT,
  877             snapshots=[fake.VG_VOLUME_SNAPSHOT])
  878 
  879         clone_source_to_destination_args = {
  880             'name': fake.VG_SNAPSHOT['name'],
  881             'size': fake.VG_SNAPSHOT['volume_size'],
  882         }
  883         mock_clone_source_to_destination.assert_called_once_with(
  884             clone_source_to_destination_args, fake.VOLUME)
  885         if volume_model_update:
  886             volume_model_update['id'] = fake.VOLUME['id']
  887         expected_return_value = ((None, [volume_model_update])
  888                                  if volume_model_update else (None, []))
  889         self.assertEqual(expected_return_value, actual_return_value)
  890 
  891     @ddt.data(None,
  892               {'replication_status': fields.ReplicationStatus.ENABLED})
  893     def test_create_group_from_src_group(self, volume_model_update):
  894         lun_name = fake.SOURCE_VG_VOLUME['name']
  895         mock_lun = block_base.NetAppLun(
  896             lun_name, lun_name, '3', {'UUID': 'fake_uuid'})
  897         self.mock_object(self.library, '_get_lun_from_table',
  898                          return_value=mock_lun)
  899         mock_clone_source_to_destination = self.mock_object(
  900             self.library, '_clone_source_to_destination',
  901             return_value=volume_model_update)
  902 
  903         actual_return_value = self.library.create_group_from_src(
  904             fake.VOLUME_GROUP, [fake.VOLUME],
  905             source_group=fake.SOURCE_VOLUME_GROUP,
  906             source_vols=[fake.SOURCE_VG_VOLUME])
  907 
  908         clone_source_to_destination_args = {
  909             'name': fake.SOURCE_VG_VOLUME['name'],
  910             'size': fake.SOURCE_VG_VOLUME['size'],
  911         }
  912         if volume_model_update:
  913             volume_model_update['id'] = fake.VOLUME['id']
  914         expected_return_value = ((None, [volume_model_update])
  915                                  if volume_model_update else (None, []))
  916         mock_clone_source_to_destination.assert_called_once_with(
  917             clone_source_to_destination_args, fake.VOLUME)
  918         self.assertEqual(expected_return_value, actual_return_value)
  919 
  920     def test_delete_group_snapshot(self):
  921         mock__delete_lun = self.mock_object(self.library, '_delete_lun')
  922 
  923         model_update, snapshots_model_update = (
  924             self.library.delete_group_snapshot(fake.VOLUME_GROUP,
  925                                                [fake.VG_SNAPSHOT]))
  926 
  927         self.assertIsNone(model_update)
  928         self.assertIsNone(snapshots_model_update)
  929 
  930         mock__delete_lun.assert_called_once_with(fake.VG_SNAPSHOT['name'])