# Copyright (c) 2014 Alex Meade.  All rights reserved.
# Copyright (c) 2014 Clinton Knight.  All rights reserved.
# Copyright (c) 2015 Tom Barron.  All rights reserved.
# Copyright (c) 2015 Goutham Pacha Ravi. All rights reserved.
# Copyright (c) 2016 Mike Rooney. All rights reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
"""
Mock unit tests for the NetApp block storage 7-mode library
"""


import ddt
from lxml import etree
import mock

from oslo_utils import timeutils

from cinder import exception
from cinder import test
import cinder.tests.unit.volume.drivers.netapp.dataontap.client.fakes \
    as client_fakes
import cinder.tests.unit.volume.drivers.netapp.dataontap.fakes as fake
import cinder.tests.unit.volume.drivers.netapp.fakes as na_fakes
from cinder.volume.drivers.netapp.dataontap import block_7mode
from cinder.volume.drivers.netapp.dataontap import block_base
from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_base
from cinder.volume.drivers.netapp.dataontap.performance import perf_7mode
from cinder.volume.drivers.netapp import utils as na_utils


@ddt.ddt
class NetAppBlockStorage7modeLibraryTestCase(test.TestCase):
    """Test case for NetApp's 7-Mode iSCSI library."""

    def setUp(self):
        super(NetAppBlockStorage7modeLibraryTestCase, self).setUp()

        kwargs = {
            'configuration': self.get_config_7mode(),
            'host': 'openstack@7modeblock',
        }
        self.library = block_7mode.NetAppBlockStorage7modeLibrary(
            'driver', 'protocol', **kwargs)

        self.library.zapi_client = mock.Mock()
        self.zapi_client = self.library.zapi_client
        self.library.perf_library = mock.Mock()
        self.library.vfiler = mock.Mock()
        # Deprecated option
        self.library.configuration.netapp_volume_list = None

    def tearDown(self):
        super(NetAppBlockStorage7modeLibraryTestCase, self).tearDown()

    def get_config_7mode(self):
        config = na_fakes.create_configuration_7mode()
        config.netapp_storage_protocol = 'iscsi'
        config.netapp_login = 'admin'
        config.netapp_password = 'pass'
        config.netapp_server_hostname = '127.0.0.1'
        config.netapp_transport_type = 'http'
        config.netapp_server_port = '80'
        return config

    @mock.patch.object(perf_7mode, 'Performance7modeLibrary', mock.Mock())
    @mock.patch.object(client_base.Client, 'get_ontapi_version',
                       mock.MagicMock(return_value=(1, 20)))
    @mock.patch.object(block_7mode.NetAppBlockStorage7modeLibrary,
                       '_get_root_volume_name')
    @mock.patch.object(block_7mode.NetAppBlockStorage7modeLibrary,
                       '_do_partner_setup')
    @mock.patch.object(block_base.NetAppBlockStorageLibrary, 'do_setup')
    def test_do_setup(self, super_do_setup, mock_do_partner_setup,
                      mock_get_root_volume_name):

        self.mock_object(client_base.Client, '_init_ssh_client')
        mock_get_root_volume_name.return_value = 'vol0'
        context = mock.Mock()

        self.library.do_setup(context)

        super_do_setup.assert_called_once_with(context)
        mock_do_partner_setup.assert_called_once_with()
        mock_get_root_volume_name.assert_called_once_with()

    @mock.patch.object(client_base.Client, 'get_ontapi_version',
                       mock.MagicMock(return_value=(1, 20)))
    def test_do_partner_setup(self):
        self.mock_object(client_base.Client, '_init_ssh_client')
        self.library.configuration.netapp_partner_backend_name = 'partner'

        self.library._do_partner_setup()

        self.assertIsNotNone(self.library.partner_zapi_client)

    @mock.patch.object(client_base.Client, 'get_ontapi_version',
                       mock.MagicMock(return_value=(1, 20)))
    def test_do_partner_setup_no_partner(self):
        self.mock_object(client_base.Client, '_init_ssh_client')
        self.library._do_partner_setup()

        self.assertFalse(hasattr(self.library, 'partner_zapi_client'))

    @mock.patch.object(
        block_base.NetAppBlockStorageLibrary, 'check_for_setup_error')
    def test_check_for_setup_error(self, super_check_for_setup_error):
        self.zapi_client.get_ontapi_version.return_value = (1, 9)
        self.mock_object(self.library, '_refresh_volume_info')
        self.library.volume_list = ['open1', 'open2']

        self.library.check_for_setup_error()

        super_check_for_setup_error.assert_called_once_with()

    def test_check_for_setup_error_no_filtered_pools(self):
        self.zapi_client.get_ontapi_version.return_value = (1, 9)
        self.mock_object(self.library, '_refresh_volume_info')
        self.library.volume_list = []

        self.assertRaises(exception.NetAppDriverException,
                          self.library.check_for_setup_error)

    @ddt.data(None, (1, 8))
    def test_check_for_setup_error_unsupported_or_no_version(self, version):
        self.zapi_client.get_ontapi_version.return_value = version
        self.assertRaises(exception.VolumeBackendAPIException,
                          self.library.check_for_setup_error)

    @ddt.data(None, fake.VFILER)
    def test__get_owner(self, vfiler):
        self.library.configuration.netapp_server_hostname = 'openstack'
        self.library.vfiler = vfiler
        expected_owner = 'openstack'

        retval = self.library._get_owner()

        if vfiler:
            expected_owner += ':' + vfiler

        self.assertEqual(expected_owner, retval)

    def test_find_mapped_lun_igroup(self):
        response = netapp_api.NaElement(etree.XML("""
<results status="passed">
    <initiator-groups>
      <initiator-group-info>
        <initiator-group-name>%(initiator-group-name)s</initiator-group-name>
        <initiator-group-type>%(initiator-group-type)s</initiator-group-type>
        <initiator-group-uuid>1477ee47-0e1f-4b35-a82c-dcca0b76fc44
        </initiator-group-uuid>
        <initiator-group-os-type>linux</initiator-group-os-type>
        <initiator-group-throttle-reserve>0</initiator-group-throttle-reserve>
        <initiator-group-throttle-borrow>false
        </initiator-group-throttle-borrow>
        <initiator-group-vsa-enabled>false</initiator-group-vsa-enabled>
        <initiator-group-alua-enabled>true</initiator-group-alua-enabled>
        <initiator-group-report-scsi-name-enabled>true
        </initiator-group-report-scsi-name-enabled>
        <initiator-group-use-partner>true</initiator-group-use-partner>
        <initiators>
          <initiator-info>
            <initiator-name>21:00:00:24:ff:40:6c:c3</initiator-name>
          </initiator-info>
          <initiator-info>
            <initiator-name>21:00:00:24:ff:40:6c:c2</initiator-name>
            <initiator-alias-info>
              <initiator-alias>Centos</initiator-alias>
            </initiator-alias-info>
          </initiator-info>
        </initiators>
        <lun-id>2</lun-id>
      </initiator-group-info>
    </initiator-groups>
  </results>""" % fake.IGROUP1))
        initiators = fake.FC_FORMATTED_INITIATORS
        self.zapi_client.get_lun_map.return_value = response

        (igroup, lun_id) = self.library._find_mapped_lun_igroup('path',
                                                                initiators)

        self.assertEqual(fake.IGROUP1_NAME, igroup)
        self.assertEqual('2', lun_id)

    def test_find_mapped_lun_igroup_initiator_mismatch(self):
        response = netapp_api.NaElement(etree.XML("""
<results status="passed">
    <initiator-groups>
      <initiator-group-info>
        <initiator-group-name>openstack-igroup1</initiator-group-name>
        <initiator-group-type>fcp</initiator-group-type>
        <initiator-group-uuid>1477ee47-0e1f-4b35-a82c-dcca0b76fc44
        </initiator-group-uuid>
        <initiator-group-os-type>linux</initiator-group-os-type>
        <initiator-group-throttle-reserve>0</initiator-group-throttle-reserve>
        <initiator-group-throttle-borrow>false
        </initiator-group-throttle-borrow>
        <initiator-group-vsa-enabled>false</initiator-group-vsa-enabled>
        <initiator-group-alua-enabled>true</initiator-group-alua-enabled>
        <initiator-group-report-scsi-name-enabled>true
        </initiator-group-report-scsi-name-enabled>
        <initiator-group-use-partner>true</initiator-group-use-partner>
        <initiators>
          <initiator-info>
            <initiator-name>21:00:00:24:ff:40:6c:c3</initiator-name>
          </initiator-info>
        </initiators>
        <lun-id>2</lun-id>
      </initiator-group-info>
    </initiator-groups>
  </results>"""))
        initiators = fake.FC_FORMATTED_INITIATORS
        self.zapi_client.get_lun_map.return_value = response

        (igroup, lun_id) = self.library._find_mapped_lun_igroup('path',
                                                                initiators)

        self.assertIsNone(igroup)
        self.assertIsNone(lun_id)

    def test_find_mapped_lun_igroup_no_igroups(self):
        response = netapp_api.NaElement(etree.XML("""
  <results status="passed">
    <initiator-groups />
  </results>"""))
        initiators = fake.FC_FORMATTED_INITIATORS
        self.zapi_client.get_lun_map.return_value = response

        (igroup, lun_id) = self.library._find_mapped_lun_igroup('path',
                                                                initiators)

        self.assertIsNone(igroup)
        self.assertIsNone(lun_id)

    def test_find_mapped_lun_igroup_raises(self):
        self.zapi_client.get_lun_map.side_effect = netapp_api.NaApiError
        initiators = fake.FC_FORMATTED_INITIATORS
        self.assertRaises(netapp_api.NaApiError,
                          self.library._find_mapped_lun_igroup,
                          'path',
                          initiators)

    def test_has_luns_mapped_to_initiators_local_map(self):
        initiator_list = fake.FC_FORMATTED_INITIATORS
        self.zapi_client.has_luns_mapped_to_initiators.return_value = True
        self.library.partner_zapi_client = mock.Mock()

        result = self.library._has_luns_mapped_to_initiators(initiator_list)

        self.assertTrue(result)
        self.zapi_client.has_luns_mapped_to_initiators.assert_called_once_with(
            initiator_list)
        self.assertEqual(0, self.library.partner_zapi_client.
                         has_luns_mapped_to_initiators.call_count)

    def test_has_luns_mapped_to_initiators_partner_map(self):
        initiator_list = fake.FC_FORMATTED_INITIATORS
        self.zapi_client.has_luns_mapped_to_initiators.return_value = False
        self.library.partner_zapi_client = mock.Mock()
        self.library.partner_zapi_client.has_luns_mapped_to_initiators.\
            return_value = True

        result = self.library._has_luns_mapped_to_initiators(initiator_list)

        self.assertTrue(result)
        self.zapi_client.has_luns_mapped_to_initiators.assert_called_once_with(
            initiator_list)
        self.library.partner_zapi_client.has_luns_mapped_to_initiators.\
            assert_called_with(initiator_list)

    def test_has_luns_mapped_to_initiators_no_maps(self):
        initiator_list = fake.FC_FORMATTED_INITIATORS
        self.zapi_client.has_luns_mapped_to_initiators.return_value = False
        self.library.partner_zapi_client = mock.Mock()
        self.library.partner_zapi_client.has_luns_mapped_to_initiators.\
            return_value = False

        result = self.library._has_luns_mapped_to_initiators(initiator_list)

        self.assertFalse(result)
        self.zapi_client.has_luns_mapped_to_initiators.assert_called_once_with(
            initiator_list)
        self.library.partner_zapi_client.has_luns_mapped_to_initiators.\
            assert_called_with(initiator_list)

    def test_has_luns_mapped_to_initiators_no_partner(self):
        initiator_list = fake.FC_FORMATTED_INITIATORS
        self.zapi_client.has_luns_mapped_to_initiators.return_value = False
        self.library.partner_zapi_client = mock.Mock()
        self.library.partner_zapi_client.has_luns_mapped_to_initiators.\
            return_value = True

        result = self.library._has_luns_mapped_to_initiators(
            initiator_list, include_partner=False)

        self.assertFalse(result)
        self.zapi_client.has_luns_mapped_to_initiators.assert_called_once_with(
            initiator_list)
        self.assertEqual(0, self.library.partner_zapi_client.
                         has_luns_mapped_to_initiators.call_count)

    @ddt.data(True, False)
    def test_clone_lun_zero_block_count(self, is_snapshot):
        """Test for when clone lun is not passed a block count."""
        self.library._get_lun_attr = mock.Mock(return_value={
            'Volume': 'fakeLUN', 'Path': '/vol/fake/fakeLUN'})
        self.library.zapi_client = mock.Mock()
        self.library.zapi_client.get_lun_by_args.return_value = [fake.FAKE_LUN]
        self.library._add_lun_to_table = mock.Mock()

        self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false',
                                is_snapshot=is_snapshot)

        self.library.zapi_client.clone_lun.assert_called_once_with(
            '/vol/fake/fakeLUN', '/vol/fake/newFakeLUN', 'fakeLUN',
            'newFakeLUN', 'false', block_count=0, dest_block=0,
            source_snapshot=None, src_block=0)

    def test_clone_lun_blocks(self):
        """Test for when clone lun is passed block information."""
        block_count = 10
        src_block = 10
        dest_block = 30
        self.library._get_lun_attr = mock.Mock(return_value={
            'Volume': 'fakeLUN', 'Path': '/vol/fake/fakeLUN'})
        self.library.zapi_client = mock.Mock()
        self.library.zapi_client.get_lun_by_args.return_value = [fake.FAKE_LUN]
        self.library._add_lun_to_table = mock.Mock()

        self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false',
                                block_count=block_count, src_block=src_block,
                                dest_block=dest_block)

        self.library.zapi_client.clone_lun.assert_called_once_with(
            '/vol/fake/fakeLUN', '/vol/fake/newFakeLUN', 'fakeLUN',
            'newFakeLUN', 'false', block_count=block_count,
            dest_block=dest_block, src_block=src_block,
            source_snapshot=None)

    def test_clone_lun_no_space_reservation(self):
        """Test for when space_reservation is not passed."""
        self.library._get_lun_attr = mock.Mock(return_value={
            'Volume': 'fakeLUN', 'Path': '/vol/fake/fakeLUN'})
        self.library.lun_space_reservation = 'false'
        self.library.zapi_client = mock.Mock()
        self.library.zapi_client.get_lun_by_args.return_value = [fake.FAKE_LUN]
        self.library._add_lun_to_table = mock.Mock()

        self.library._clone_lun('fakeLUN', 'newFakeLUN')

        self.library.zapi_client.clone_lun.assert_called_once_with(
            '/vol/fake/fakeLUN', '/vol/fake/newFakeLUN', 'fakeLUN',
            'newFakeLUN', 'false', block_count=0, dest_block=0, src_block=0,
            source_snapshot=None)

    def test_clone_lun_qos_supplied(self):
        """Test for qos supplied in clone lun invocation."""
        self.assertRaises(exception.VolumeDriverException,
                          self.library._clone_lun,
                          'fakeLUN',
                          'newFakeLUN',
                          qos_policy_group_name=fake.QOS_POLICY_GROUP_NAME)

    def test_get_fc_target_wwpns(self):
        ports1 = [fake.FC_FORMATTED_TARGET_WWPNS[0],
                  fake.FC_FORMATTED_TARGET_WWPNS[1]]
        ports2 = [fake.FC_FORMATTED_TARGET_WWPNS[2],
                  fake.FC_FORMATTED_TARGET_WWPNS[3]]
        self.zapi_client.get_fc_target_wwpns.return_value = ports1
        self.library.partner_zapi_client = mock.Mock()
        self.library.partner_zapi_client.get_fc_target_wwpns.return_value = \
            ports2

        result = self.library._get_fc_target_wwpns()

        self.assertSetEqual(set(fake.FC_FORMATTED_TARGET_WWPNS), set(result))

    def test_get_fc_target_wwpns_no_partner(self):
        ports1 = [fake.FC_FORMATTED_TARGET_WWPNS[0],
                  fake.FC_FORMATTED_TARGET_WWPNS[1]]
        ports2 = [fake.FC_FORMATTED_TARGET_WWPNS[2],
                  fake.FC_FORMATTED_TARGET_WWPNS[3]]
        self.zapi_client.get_fc_target_wwpns.return_value = ports1
        self.library.partner_zapi_client = mock.Mock()
        self.library.partner_zapi_client.get_fc_target_wwpns.return_value = \
            ports2

        result = self.library._get_fc_target_wwpns(include_partner=False)

        self.assertSetEqual(set(ports1), set(result))

    @mock.patch.object(block_7mode.NetAppBlockStorage7modeLibrary,
                       '_refresh_volume_info', mock.Mock())
    @mock.patch.object(block_7mode.NetAppBlockStorage7modeLibrary,
                       '_get_pool_stats', mock.Mock())
    def test_vol_stats_calls_provide_ems(self):
        self.library.zapi_client.provide_ems = mock.Mock()

        self.library.get_volume_stats(refresh=True)

        self.assertEqual(1, self.library.zapi_client.provide_ems.call_count)

    def test_create_lun(self):
        self.library.vol_refresh_voluntary = False

        self.library._create_lun(fake.VOLUME_ID, fake.LUN_ID,
                                 fake.LUN_SIZE, fake.LUN_METADATA)

        self.library.zapi_client.create_lun.assert_called_once_with(
            fake.VOLUME_ID, fake.LUN_ID, fake.LUN_SIZE, fake.LUN_METADATA,
            None)
        self.assertTrue(self.library.vol_refresh_voluntary)

    def test_create_lun_with_qos_policy_group(self):
        self.assertRaises(exception.VolumeDriverException,
                          self.library._create_lun, fake.VOLUME_ID,
                          fake.LUN_ID, fake.LUN_SIZE, fake.LUN_METADATA,
                          qos_policy_group_name=fake.QOS_POLICY_GROUP_NAME)

    def test_check_volume_type_for_lun_legacy_qos_not_supported(self):
        mock_get_volume_type = self.mock_object(na_utils,
                                                'get_volume_type_from_volume')

        self.assertRaises(exception.ManageExistingVolumeTypeMismatch,
                          self.library._check_volume_type_for_lun,
                          na_fakes.VOLUME, {}, {}, na_fakes.LEGACY_EXTRA_SPECS)

        self.assertEqual(0, mock_get_volume_type.call_count)

    def test_check_volume_type_for_lun_no_volume_type(self):
        mock_get_volume_type = self.mock_object(na_utils,
                                                'get_volume_type_from_volume')
        mock_get_volume_type.return_value = None
        mock_get_backend_spec = self.mock_object(
            na_utils, 'get_backend_qos_spec_from_volume_type')

        self.library._check_volume_type_for_lun(na_fakes.VOLUME, {}, {}, None)

        self.assertEqual(0, mock_get_backend_spec.call_count)

    def test_check_volume_type_for_lun_qos_spec_not_supported(self):
        mock_get_volume_type = self.mock_object(na_utils,
                                                'get_volume_type_from_volume')
        mock_get_volume_type.return_value = na_fakes.VOLUME_TYPE
        mock_get_backend_spec = self.mock_object(
            na_utils, 'get_backend_qos_spec_from_volume_type')
        mock_get_backend_spec.return_value = na_fakes.QOS_SPEC

        self.assertRaises(exception.ManageExistingVolumeTypeMismatch,
                          self.library._check_volume_type_for_lun,
                          na_fakes.VOLUME, {}, {}, na_fakes.EXTRA_SPECS)

    def test_get_preferred_target_from_list(self):

        result = self.library._get_preferred_target_from_list(
            fake.ISCSI_TARGET_DETAILS_LIST)

        self.assertEqual(fake.ISCSI_TARGET_DETAILS_LIST[0], result)

    def test_mark_qos_policy_group_for_deletion(self):
        result = self.library._mark_qos_policy_group_for_deletion(
            fake.QOS_POLICY_GROUP_INFO)

        self.assertIsNone(result)

    def test_setup_qos_for_volume(self):
        result = self.library._setup_qos_for_volume(fake.VOLUME,
                                                    fake.EXTRA_SPECS)

        self.assertIsNone(result)

    def test_manage_existing_lun_same_name(self):
        mock_lun = block_base.NetAppLun('handle', 'name', '1',
                                        {'Path': '/vol/FAKE_CMODE_VOL1/name'})
        self.library._get_existing_vol_with_manage_ref = mock.Mock(
            return_value=mock_lun)
        self.mock_object(na_utils, 'get_volume_extra_specs')
        self.mock_object(na_utils, 'log_extra_spec_warnings')
        self.library._check_volume_type_for_lun = mock.Mock()
        self.library._add_lun_to_table = mock.Mock()
        self.zapi_client.move_lun = mock.Mock()

        self.library.manage_existing({'name': 'name'}, {'ref': 'ref'})

        self.library._get_existing_vol_with_manage_ref.assert_called_once_with(
            {'ref': 'ref'})
        self.assertEqual(1, self.library._check_volume_type_for_lun.call_count)
        self.assertEqual(1, self.library._add_lun_to_table.call_count)
        self.assertEqual(0, self.zapi_client.move_lun.call_count)

    def test_manage_existing_lun_new_path(self):
        mock_lun = block_base.NetAppLun(
            'handle', 'name', '1', {'Path': '/vol/FAKE_CMODE_VOL1/name'})
        self.library._get_existing_vol_with_manage_ref = mock.Mock(
            return_value=mock_lun)
        self.mock_object(na_utils, 'get_volume_extra_specs')
        self.mock_object(na_utils, 'log_extra_spec_warnings')
        self.library._check_volume_type_for_lun = mock.Mock()
        self.library._add_lun_to_table = mock.Mock()
        self.zapi_client.move_lun = mock.Mock()

        self.library.manage_existing({'name': 'volume'}, {'ref': 'ref'})

        self.assertEqual(
            2, self.library._get_existing_vol_with_manage_ref.call_count)
        self.assertEqual(1, self.library._check_volume_type_for_lun.call_count)
        self.assertEqual(1, self.library._add_lun_to_table.call_count)
        self.zapi_client.move_lun.assert_called_once_with(
            '/vol/FAKE_CMODE_VOL1/name', '/vol/FAKE_CMODE_VOL1/volume')

    def test_get_pool_stats_no_volumes(self):

        self.library.vols = []

        result = self.library._get_pool_stats()

        self.assertListEqual([], result)

    @ddt.data({'netapp_lun_space_reservation': 'enabled'},
              {'netapp_lun_space_reservation': 'disabled'})
    @ddt.unpack
    def test_get_pool_stats(self, netapp_lun_space_reservation):

        self.library.volume_list = ['vol0', 'vol1', 'vol2']
        self.library.root_volume_name = 'vol0'
        self.library.reserved_percentage = 5
        self.library.max_over_subscription_ratio = 10.0
        self.library.configuration.netapp_lun_space_reservation = (
            netapp_lun_space_reservation)
        self.library.vols = netapp_api.NaElement(
            client_fakes.VOLUME_LIST_INFO_RESPONSE).get_child_by_name(
            'volumes').get_children()
        self.library.perf_library.get_node_utilization = (
            mock.Mock(return_value=30.0))

        thick = netapp_lun_space_reservation == 'enabled'

        result = self.library._get_pool_stats(filter_function='filter',
                                              goodness_function='goodness')

        expected = [{
            'pool_name': 'vol1',
            'consistencygroup_support': True,
            'QoS_support': False,
            'thin_provisioning_support': not thick,
            'thick_provisioning_support': thick,
            'provisioned_capacity_gb': 2.94,
            'free_capacity_gb': 1339.27,
            'total_capacity_gb': 1342.21,
            'reserved_percentage': 5,
            'max_over_subscription_ratio': 10.0,
            'multiattach': True,
            'utilization': 30.0,
            'filter_function': 'filter',
            'goodness_function': 'goodness',
        }]

        self.assertEqual(expected, result)

    def test_get_filtered_pools_invalid_conf(self):
        """Verify an exception is raised if the regex pattern is invalid."""
        self.library.configuration.netapp_pool_name_search_pattern = '(.+'

        self.assertRaises(exception.InvalidConfigurationValue,
                          self.library._get_filtered_pools)

    @ddt.data('.*?3$|mix.+', '(.+?[0-9]+) ', '^.+3$', '^[a-z].*?[^4]$')
    def test_get_filtered_pools_match_select_pools(self, patterns):
        self.library.vols = fake.FAKE_7MODE_VOLUME['all']
        self.library.configuration.netapp_pool_name_search_pattern = patterns

        filtered_pools = self.library._get_filtered_pools()

        self.assertEqual(
            fake.FAKE_7MODE_VOLUME['all'][0].get_child_content('name'),
            filtered_pools[0]
        )
        self.assertEqual(
            fake.FAKE_7MODE_VOLUME['all'][1].get_child_content('name'),
            filtered_pools[1]
        )

    @ddt.data('', 'mix.+|open.+', '.+', 'open123, mixed3, open1234', '.+')
    def test_get_filtered_pools_match_all_pools(self, patterns):
        self.library.vols = fake.FAKE_7MODE_VOLUME['all']
        self.library.configuration.netapp_pool_name_search_pattern = patterns

        filtered_pools = self.library._get_filtered_pools()

        self.assertEqual(
            fake.FAKE_7MODE_VOLUME['all'][0].get_child_content('name'),
            filtered_pools[0]
        )
        self.assertEqual(
            fake.FAKE_7MODE_VOLUME['all'][1].get_child_content('name'),
            filtered_pools[1]
        )
        self.assertEqual(
            fake.FAKE_7MODE_VOLUME['all'][2].get_child_content('name'),
            filtered_pools[2]
        )

    @ddt.data('abc|stackopen|openstack|abc.*', 'abc',
              'stackopen, openstack, open', '^$')
    def test_get_filtered_pools_non_matching_patterns(self, patterns):

        self.library.vols = fake.FAKE_7MODE_VOLUME['all']
        self.library.configuration.netapp_pool_name_search_pattern = patterns

        filtered_pools = self.library._get_filtered_pools()

        self.assertListEqual([], filtered_pools)

    def test_get_pool_stats_no_ssc_vols(self):

        self.library.vols = {}

        pools = self.library._get_pool_stats()

        self.assertListEqual([], pools)

    def test_get_pool_stats_with_filtered_pools(self):

        self.library.vols = fake.FAKE_7MODE_VOL1
        self.library.volume_list = [
            fake.FAKE_7MODE_VOL1[0].get_child_content('name')
        ]
        self.library.root_volume_name = ''
        self.library.perf_library.get_node_utilization = (
            mock.Mock(return_value=30.0))

        pools = self.library._get_pool_stats(filter_function='filter',
                                             goodness_function='goodness')

        self.assertListEqual(fake.FAKE_7MODE_POOLS, pools)

    def test_get_pool_stats_no_filtered_pools(self):

        self.library.vols = fake.FAKE_7MODE_VOL1
        self.library.volume_list = ['open1', 'open2']
        self.library.root_volume_name = ''

        pools = self.library._get_pool_stats()

        self.assertListEqual([], pools)

    @ddt.data((None, False, False),
              (30, True, False),
              (30, False, True))
    @ddt.unpack
    def test__refresh_volume_info_already_running(self,
                                                  vol_refresh_time,
                                                  vol_refresh_voluntary,
                                                  is_newer):
        mock_warning_log = self.mock_object(block_7mode.LOG, 'warning')
        self.library.vol_refresh_time = vol_refresh_time
        self.library.vol_refresh_voluntary = vol_refresh_voluntary
        self.library.vol_refresh_interval = 30
        self.mock_object(timeutils, 'is_newer_than', mock.Mock(
            return_value=is_newer))
        self.mock_object(na_utils, 'set_safe_attr', mock.Mock(
            return_value=False))

        retval = self.library._refresh_volume_info()

        self.assertIsNone(retval)
        # Assert no values are unset by the method
        self.assertEqual(vol_refresh_voluntary,
                         self.library.vol_refresh_voluntary)
        self.assertEqual(vol_refresh_time, self.library.vol_refresh_time)
        if timeutils.is_newer_than.called:
            timeutils.is_newer_than.assert_called_once_with(
                vol_refresh_time, self.library.vol_refresh_interval)
        na_utils.set_safe_attr.assert_has_calls([
            mock.call(self.library, 'vol_refresh_running', True),
            mock.call(self.library, 'vol_refresh_running', False)])
        self.assertEqual(1, mock_warning_log.call_count)

    def test__refresh_volume_info(self):
        mock_warning_log = self.mock_object(block_7mode.LOG, 'warning')
        self.library.vol_refresh_time = None
        self.library.vol_refresh_voluntary = True
        self.mock_object(timeutils, 'is_newer_than')
        self.mock_object(self.library.zapi_client, 'get_filer_volumes')
        self.mock_object(self.library, '_get_filtered_pools', mock.Mock(
            return_value=['vol1', 'vol2']))
        self.mock_object(na_utils, 'set_safe_attr', mock.Mock(
            return_value=True))

        retval = self.library._refresh_volume_info()

        self.assertIsNone(retval)
        self.assertEqual(False, self.library.vol_refresh_voluntary)
        self.assertEqual(['vol1', 'vol2'], self.library.volume_list)
        self.assertIsNotNone(self.library.vol_refresh_time)
        na_utils.set_safe_attr.assert_has_calls([
            mock.call(self.library, 'vol_refresh_running', True),
            mock.call(self.library, 'vol_refresh_running', False)])
        self.assertFalse(mock_warning_log.called)

    def test__refresh_volume_info_exception(self):
        mock_warning_log = self.mock_object(block_7mode.LOG, 'warning')
        self.library.vol_refresh_time = None
        self.library.vol_refresh_voluntary = True
        self.mock_object(timeutils, 'is_newer_than')
        self.mock_object(na_utils, 'set_safe_attr', mock.Mock(
            return_value=True))
        self.mock_object(
            self.library.zapi_client, 'get_filer_volumes',
            mock.Mock(side_effect=exception.NetAppDriverException))
        self.mock_object(self.library, '_get_filtered_pools')

        retval = self.library._refresh_volume_info()

        self.assertIsNone(retval)
        self.assertFalse(self.library._get_filtered_pools.called)
        self.assertEqual(1, mock_warning_log.call_count)

    def test_delete_volume(self):
        self.library.vol_refresh_voluntary = False
        mock_super_delete_volume = self.mock_object(
            block_base.NetAppBlockStorageLibrary, 'delete_volume')

        self.library.delete_volume(fake.VOLUME)

        mock_super_delete_volume.assert_called_once_with(fake.VOLUME)
        self.assertTrue(self.library.vol_refresh_voluntary)

    def test_delete_snapshot(self):
        self.library.vol_refresh_voluntary = False
        mock_super_delete_snapshot = self.mock_object(
            block_base.NetAppBlockStorageLibrary, 'delete_snapshot')

        self.library.delete_snapshot(fake.SNAPSHOT)

        mock_super_delete_snapshot.assert_called_once_with(fake.SNAPSHOT)
        self.assertTrue(self.library.vol_refresh_voluntary)
