#
#    (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
#    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.
"""Unit tests for OpenStack Cinder volume drivers."""

import mock

import ast

from oslo.config import cfg

from cinder import context
from cinder import exception
from cinder.openstack.common import log as logging
from cinder.openstack.common import units
from cinder import test
from cinder.tests import fake_hp_3par_client as hp3parclient
from cinder.volume.drivers.san.hp import hp_3par_common as hpcommon
from cinder.volume.drivers.san.hp import hp_3par_fc as hpfcdriver
from cinder.volume.drivers.san.hp import hp_3par_iscsi as hpdriver
from cinder.volume import qos_specs
from cinder.volume import volume_types

hpexceptions = hp3parclient.hpexceptions

LOG = logging.getLogger(__name__)

CONF = cfg.CONF

HP3PAR_CPG = 'OpenStackCPG'
HP3PAR_CPG_SNAP = 'OpenStackCPGSnap'
HP3PAR_USER_NAME = 'testUser'
HP3PAR_USER_PASS = 'testPassword'
HP3PAR_SAN_IP = '2.2.2.2'
HP3PAR_SAN_SSH_PORT = 999
HP3PAR_SAN_SSH_CON_TIMEOUT = 44
HP3PAR_SAN_SSH_PRIVATE = 'foobar'

CHAP_USER_KEY = "HPQ-cinder-CHAP-name"
CHAP_PASS_KEY = "HPQ-cinder-CHAP-secret"


class HP3PARBaseDriver(object):

    class CommentMatcher(object):
        def __init__(self, f, expect):
            self.assertEqual = f
            self.expect = expect

        def __eq__(self, actual):
            actual_as_dict = dict(ast.literal_eval(actual))
            self.assertEqual(self.expect, actual_as_dict)
            return True

    VOLUME_ID = 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'
    CLONE_ID = 'd03338a9-9115-48a3-8dfc-000000000000'
    VOLUME_NAME = 'volume-' + VOLUME_ID
    VOLUME_NAME_3PAR = 'osv-0DM4qZEVSKON-DXN-NwVpw'
    SNAPSHOT_ID = '2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
    SNAPSHOT_NAME = 'snapshot-2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
    VOLUME_3PAR_NAME = 'osv-0DM4qZEVSKON-DXN-NwVpw'
    SNAPSHOT_3PAR_NAME = 'oss-L4I73ONuTci9Fd4ceij-MQ'
    FAKE_HOST = 'fakehost'
    USER_ID = '2689d9a913974c008b1d859013f23607'
    PROJECT_ID = 'fac88235b9d64685a3530f73e490348f'
    VOLUME_ID_SNAP = '761fc5e5-5191-4ec7-aeba-33e36de44156'
    FAKE_DESC = 'test description name'
    FAKE_FC_PORTS = [{'portPos': {'node': 7, 'slot': 1, 'cardPort': 1},
                      'portWWN': '0987654321234',
                      'protocol': 1,
                      'mode': 2,
                      'linkState': 4},
                     {'portPos': {'node': 6, 'slot': 1, 'cardPort': 1},
                      'portWWN': '123456789000987',
                      'protocol': 1,
                      'mode': 2,
                      'linkState': 4}]
    QOS = {'qos:maxIOPS': '1000', 'qos:maxBWS': '50',
           'qos:minIOPS': '100', 'qos:minBWS': '25',
           'qos:latency': '25', 'qos:priority': 'low'}
    QOS_SPECS = {'maxIOPS': '1000', 'maxBWS': '50',
                 'minIOPS': '100', 'minBWS': '25',
                 'latency': '25', 'priority': 'low'}
    VVS_NAME = "myvvs"
    FAKE_ISCSI_PORT = {'portPos': {'node': 8, 'slot': 1, 'cardPort': 1},
                       'protocol': 2,
                       'mode': 2,
                       'IPAddr': '1.1.1.2',
                       'iSCSIName': ('iqn.2000-05.com.3pardata:'
                                     '21810002ac00383d'),
                       'linkState': 4}
    volume = {'name': VOLUME_NAME,
              'id': VOLUME_ID,
              'display_name': 'Foo Volume',
              'size': 2,
              'host': FAKE_HOST,
              'volume_type': None,
              'volume_type_id': None}

    volume_qos = {'name': VOLUME_NAME,
                  'id': VOLUME_ID,
                  'display_name': 'Foo Volume',
                  'size': 2,
                  'host': FAKE_HOST,
                  'volume_type': None,
                  'volume_type_id': 'gold'}

    snapshot = {'name': SNAPSHOT_NAME,
                'id': SNAPSHOT_ID,
                'user_id': USER_ID,
                'project_id': PROJECT_ID,
                'volume_id': VOLUME_ID_SNAP,
                'volume_name': VOLUME_NAME,
                'status': 'creating',
                'progress': '0%',
                'volume_size': 2,
                'display_name': 'fakesnap',
                'display_description': FAKE_DESC}

    wwn = ["123456789012345", "123456789054321"]

    connector = {'ip': '10.0.0.2',
                 'initiator': 'iqn.1993-08.org.debian:01:222',
                 'wwpns': [wwn[0], wwn[1]],
                 'wwnns': ["223456789012345", "223456789054321"],
                 'host': FAKE_HOST}

    volume_type = {'name': 'gold',
                   'deleted': False,
                   'updated_at': None,
                   'extra_specs': {'qos:maxIOPS': '1000',
                                   'qos:maxBWS': '50',
                                   'qos:minIOPS': '100',
                                   'qos:minBWS': '25',
                                   'qos:latency': '25',
                                   'qos:priority': 'low'},
                   'deleted_at': None,
                   'id': 'gold'}

    cpgs = [
        {'SAGrowth': {'LDLayout': {'diskPatterns': [{'diskType': 2}]},
                      'incrementMiB': 8192},
         'SAUsage': {'rawTotalMiB': 24576,
                     'rawUsedMiB': 768,
                     'totalMiB': 8192,
                     'usedMiB': 256},
         'SDGrowth': {'LDLayout': {'RAIDType': 4,
                      'diskPatterns': [{'diskType': 2}]},
                      'incrementMiB': 32768},
         'SDUsage': {'rawTotalMiB': 49152,
                     'rawUsedMiB': 1023,
                     'totalMiB': 36864,
                     'usedMiB': 768},
         'UsrUsage': {'rawTotalMiB': 57344,
                      'rawUsedMiB': 43349,
                      'totalMiB': 43008,
                      'usedMiB': 32512},
         'additionalStates': [],
         'degradedStates': [],
         'failedStates': [],
         'id': 5,
         'name': HP3PAR_CPG,
         'numFPVVs': 2,
         'numTPVVs': 0,
         'state': 1,
         'uuid': '29c214aa-62b9-41c8-b198-543f6cf24edf'}]

    TASK_DONE = 1
    TASK_ACTIVE = 2
    STATUS_DONE = {'status': 1}
    STATUS_ACTIVE = {'status': 2}

    mock_client_conf = {
        'PORT_MODE_TARGET': 2,
        'PORT_STATE_READY': 4,
        'PORT_PROTO_ISCSI': 2,
        'PORT_PROTO_FC': 1,
        'TASK_DONE': TASK_DONE,
        'TASK_ACTIVE': TASK_ACTIVE,
        'HOST_EDIT_ADD': 1,
        'CHAP_INITIATOR': 1,
        'CHAP_TARGET': 2,
        'getPorts.return_value': {
            'members': FAKE_FC_PORTS + [FAKE_ISCSI_PORT]
        }
    }

    RETYPE_VVS_NAME = "yourvvs"

    RETYPE_HOST = {
        u'host': u'mark-stack1@3parfc',
        u'capabilities': {
            'QoS_support': True,
            u'location_info': u'HP3PARDriver:1234567:MARK_TEST_CPG',
            u'timestamp': u'2014-06-04T19:03:32.485540',
            u'allocated_capacity_gb': 0,
            u'volume_backend_name': u'3parfc',
            u'free_capacity_gb': u'infinite',
            u'driver_version': u'2.0.3',
            u'total_capacity_gb': u'infinite',
            u'reserved_percentage': 0,
            u'vendor_name': u'Hewlett-Packard',
            u'storage_protocol': u'FC'
        }
    }

    RETYPE_HOST_NOT3PAR = {
        u'host': u'mark-stack1@3parfc',
        u'capabilities': {
            u'location_info': u'XXXDriverXXX:1610771:MARK_TEST_CPG',
        }
    }

    RETYPE_QOS_SPECS = {'maxIOPS': '1000', 'maxBWS': '50',
                        'minIOPS': '100', 'minBWS': '25',
                        'latency': '25', 'priority': 'high'}

    RETYPE_VOLUME_TYPE_ID = "FakeVolId"

    RETYPE_VOLUME_TYPE_0 = {
        'name': 'red',
        'id': RETYPE_VOLUME_TYPE_ID,
        'extra_specs': {
            'cpg': HP3PAR_CPG,
            'snap_cpg': HP3PAR_CPG_SNAP,
            'vvs': RETYPE_VVS_NAME,
            'qos': RETYPE_QOS_SPECS,
            'tpvv': True,
            'volume_type': volume_type
        }
    }

    RETYPE_VOLUME_TYPE_1 = {
        'name': 'white',
        'id': RETYPE_VOLUME_TYPE_ID,
        'extra_specs': {
            'cpg': HP3PAR_CPG,
            'snap_cpg': HP3PAR_CPG_SNAP,
            'vvs': VVS_NAME,
            'qos': QOS,
            'tpvv': True,
            'volume_type': volume_type
        }
    }

    RETYPE_VOLUME_TYPE_2 = {
        'name': 'blue',
        'id': RETYPE_VOLUME_TYPE_ID,
        'extra_specs': {
            'cpg': HP3PAR_CPG,
            'snap_cpg': HP3PAR_CPG_SNAP,
            'vvs': RETYPE_VVS_NAME,
            'qos': RETYPE_QOS_SPECS,
            'tpvv': True,
            'volume_type': volume_type
        }
    }

    RETYPE_VOLUME_TYPE_BAD_PERSONA = {
        'name': 'bad_persona',
        'id': 'any_id',
        'extra_specs': {
            'hp3par:persona': '99 - invalid'
        }
    }

    RETYPE_VOLUME_TYPE_BAD_CPG = {
        'name': 'bad_cpg',
        'id': 'any_id',
        'extra_specs': {
            'cpg': 'bogus',
            'snap_cpg': 'bogus',
            'hp3par:persona': '2 - Generic-ALUA'
        }
    }

    MANAGE_VOLUME_INFO = {
        'userCPG': 'testUserCpg0',
        'snapCPG': 'testSnapCpg0',
        'provisioningType': 1,
        'comment': "{'display_name': 'Foo Volume'}"
    }

    RETYPE_TEST_COMMENT = "{'retype_test': 'test comment'}"

    RETYPE_VOLUME_INFO_0 = {
        'name': VOLUME_NAME,
        'id': VOLUME_ID,
        'display_name': 'Retype Vol0',
        'size': 1,
        'host': RETYPE_HOST,
        'userCPG': 'testUserCpg0',
        'snapCPG': 'testSnapCpg0',
        'provisioningType': 1,
        'comment': RETYPE_TEST_COMMENT
    }

    RETYPE_TEST_COMMENT_1 = "{'retype_test': 'test comment 1'}"

    RETYPE_VOLUME_INFO_1 = {
        'name': VOLUME_NAME,
        'id': VOLUME_ID,
        'display_name': 'Retype Vol1',
        'size': 1,
        'host': RETYPE_HOST,
        'userCPG': HP3PAR_CPG,
        'snapCPG': HP3PAR_CPG_SNAP,
        'provisioningType': 1,
        'comment': RETYPE_TEST_COMMENT
    }

    # Test for when we don't get a snapCPG.
    RETYPE_VOLUME_INFO_NO_SNAP = {
        'name': VOLUME_NAME,
        'id': VOLUME_ID,
        'display_name': 'Retype Vol2',
        'size': 1,
        'host': RETYPE_HOST,
        'userCPG': 'testUserCpg2',
        'provisioningType': 1,
        'comment': '{}'
    }

    RETYPE_CONF = {
        'TASK_ACTIVE': TASK_ACTIVE,
        'TASK_DONE': TASK_DONE,
        'getTask.return_value': STATUS_DONE,
        'getStorageSystemInfo.return_value': {'serialNumber': '1234567'},
        'getVolume.return_value': RETYPE_VOLUME_INFO_0,
        'modifyVolume.return_value': ("anyResponse", {'taskid': 1})
    }

    # 3PAR retype currently doesn't use the diff.  Existing code and fresh info
    # from the array work better for the most part.  Some use of the diff was
    # intentionally removed to make _retype more usable for other use cases.
    RETYPE_DIFF = None

    def setup_configuration(self):
        configuration = mock.Mock()
        configuration.hp3par_debug = False
        configuration.hp3par_username = HP3PAR_USER_NAME
        configuration.hp3par_password = HP3PAR_USER_PASS
        configuration.hp3par_api_url = 'https://1.1.1.1/api/v1'
        configuration.hp3par_cpg = HP3PAR_CPG
        configuration.hp3par_cpg_snap = HP3PAR_CPG_SNAP
        configuration.iscsi_ip_address = '1.1.1.2'
        configuration.iscsi_port = '1234'
        configuration.san_ip = HP3PAR_SAN_IP
        configuration.san_login = HP3PAR_USER_NAME
        configuration.san_password = HP3PAR_USER_PASS
        configuration.san_ssh_port = HP3PAR_SAN_SSH_PORT
        configuration.ssh_conn_timeout = HP3PAR_SAN_SSH_CON_TIMEOUT
        configuration.san_private_key = HP3PAR_SAN_SSH_PRIVATE
        configuration.hp3par_snapshot_expiration = ""
        configuration.hp3par_snapshot_retention = ""
        configuration.hp3par_iscsi_ips = []
        configuration.hp3par_iscsi_chap_enabled = False
        return configuration

    @mock.patch(
        'hp3parclient.client.HP3ParClient',
        spec=True,
    )
    def setup_mock_client(self, _m_client, driver, conf=None, m_conf=None):

        _m_client = _m_client.return_value

        # Configure the base constants, defaults etc...
        _m_client.configure_mock(**self.mock_client_conf)

        # If m_conf, drop those over the top of the base_conf.
        if m_conf is not None:
            _m_client.configure_mock(**m_conf)

        if conf is None:
            conf = self.setup_configuration()
        self.driver = driver(configuration=conf)
        self.driver.do_setup(None)
        return _m_client

    @mock.patch('hp3parclient.version', "3.0.9")
    def test_unsupported_client_version(self):

        self.assertRaises(exception.InvalidInput,
                          self.setup_driver)

    @mock.patch('hp3parclient.version', "3.1.0")
    def test_ssh_options_310(self):

        self.ctxt = context.get_admin_context()
        mock_client = self.setup_mock_client(driver=hpfcdriver.HP3PARFCDriver)
        expected = [
            mock.call.setSSHOptions(
                HP3PAR_SAN_IP,
                HP3PAR_USER_NAME,
                HP3PAR_USER_PASS,
                privatekey=HP3PAR_SAN_SSH_PRIVATE,
                port=HP3PAR_SAN_SSH_PORT,
                conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)

    @mock.patch('hp3parclient.version', "3.1.1")
    def test_ssh_options(self):

        expected_hosts_key_file = "test_hosts_key_file"
        orig_ssh_hosts_key_file = CONF.ssh_hosts_key_file
        orig_strict_ssh_host_key_policy = CONF.strict_ssh_host_key_policy
        CONF.ssh_hosts_key_file = expected_hosts_key_file
        CONF.strict_ssh_host_key_policy = False

        self.ctxt = context.get_admin_context()
        mock_client = self.setup_mock_client(driver=hpfcdriver.HP3PARFCDriver)

        CONF.ssh_hosts_key_file = orig_ssh_hosts_key_file
        CONF.strict_ssh_host_key_policy = orig_strict_ssh_host_key_policy

        expected = [
            mock.call.setSSHOptions(
                HP3PAR_SAN_IP,
                HP3PAR_USER_NAME,
                HP3PAR_USER_PASS,
                privatekey=HP3PAR_SAN_SSH_PRIVATE,
                known_hosts_file=expected_hosts_key_file,
                missing_key_policy="AutoAddPolicy",
                port=HP3PAR_SAN_SSH_PORT,
                conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)

    @mock.patch('hp3parclient.version', "3.1.1")
    def test_ssh_options_strict(self):

        expected_hosts_key_file = "test_hosts_key_file"
        orig_ssh_hosts_key_file = CONF.ssh_hosts_key_file
        orig_strict_ssh_host_key_policy = CONF.strict_ssh_host_key_policy
        CONF.ssh_hosts_key_file = expected_hosts_key_file
        CONF.strict_ssh_host_key_policy = True

        self.ctxt = context.get_admin_context()
        mock_client = self.setup_mock_client(driver=hpfcdriver.HP3PARFCDriver)

        CONF.ssh_hosts_key_file = orig_ssh_hosts_key_file
        CONF.strict_ssh_host_key_policy = orig_strict_ssh_host_key_policy

        expected = [
            mock.call.setSSHOptions(
                HP3PAR_SAN_IP,
                HP3PAR_USER_NAME,
                HP3PAR_USER_PASS,
                privatekey=HP3PAR_SAN_SSH_PRIVATE,
                known_hosts_file=expected_hosts_key_file,
                missing_key_policy="RejectPolicy",
                port=HP3PAR_SAN_SSH_PORT,
                conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)

    def test_task_waiter(self):

        task_statuses = [self.STATUS_ACTIVE, self.STATUS_ACTIVE]

        def side_effect(*args):
            return task_statuses and task_statuses.pop(0) or self.STATUS_DONE

        conf = {'getTask.side_effect': side_effect}
        mock_client = self.setup_driver(mock_conf=conf)

        task_id = 1234
        interval = .001
        waiter = self.driver.common.TaskWaiter(mock_client, task_id, interval)
        status = waiter.wait_for_task()

        expected = [
            mock.call.getTask(task_id),
            mock.call.getTask(task_id),
            mock.call.getTask(task_id)
        ]
        mock_client.assert_has_calls(expected)
        self.assertEqual(status, self.STATUS_DONE)

    def test_create_volume(self):

        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        self.driver.create_volume(self.volume)
        comment = (
            '{"display_name": "Foo Volume", "type": "OpenStack",'
            ' "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",'
            ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')
        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.createVolume(
                self.VOLUME_3PAR_NAME,
                HP3PAR_CPG,
                1907, {
                    'comment': comment,
                    'tpvv': True,
                    'snapCPG': HP3PAR_CPG_SNAP}),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_create_volume_qos(self, _mock_volume_types):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        _mock_volume_types.return_value = {
            'name': 'gold',
            'extra_specs': {
                'cpg': HP3PAR_CPG,
                'snap_cpg': HP3PAR_CPG_SNAP,
                'vvs_name': self.VVS_NAME,
                'qos': self.QOS,
                'tpvv': True,
                'volume_type': self.volume_type}}

        self.driver.create_volume(self.volume_qos)
        comment = (
            '{"volume_type_name": "gold", "display_name": "Foo Volume"'
            ', "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7'
            '", "volume_type_id": "gold", "volume_id": "d03338a9-91'
            '15-48a3-8dfc-35cdfcdc15a7", "qos": {}, "type": "OpenStack"}')

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.createVolume(
                self.VOLUME_3PAR_NAME,
                HP3PAR_CPG,
                1907, {
                    'comment': comment,
                    'tpvv': True,
                    'snapCPG': HP3PAR_CPG_SNAP}),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_not_3par(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)

        self.assertRaises(exception.InvalidHost,
                          self.driver.retype,
                          self.ctxt,
                          self.RETYPE_VOLUME_INFO_0,
                          self.RETYPE_VOLUME_TYPE_1,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST_NOT3PAR)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_volume_not_found(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
        mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound

        self.assertRaises(hpexceptions.HTTPNotFound,
                          self.driver.retype,
                          self.ctxt,
                          self.RETYPE_VOLUME_INFO_0,
                          self.RETYPE_VOLUME_TYPE_1,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_specs_error_reverts_snap_cpg(self, _mock_volume_types):
        _mock_volume_types.side_effect = [
            self.RETYPE_VOLUME_TYPE_1, self.RETYPE_VOLUME_TYPE_0]
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
        mock_client.getVolume.return_value = self.RETYPE_VOLUME_INFO_0

        # Fail the QOS setting to test the revert of the snap CPG rename.
        mock_client.addVolumeToVolumeSet.side_effect = \
            hpexceptions.HTTPForbidden

        self.assertRaises(hpexceptions.HTTPForbidden,
                          self.driver.retype,
                          self.ctxt,
                          {'id': self.VOLUME_ID},
                          self.RETYPE_VOLUME_TYPE_0,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST)

        old_settings = {
            'snapCPG': self.RETYPE_VOLUME_INFO_0['snapCPG'],
            'comment': self.RETYPE_VOLUME_INFO_0['comment']}
        new_settings = {
            'snapCPG': self.RETYPE_VOLUME_TYPE_1['extra_specs']['snap_cpg'],
            'comment': mock.ANY}

        expected = [
            mock.call.modifyVolume(self.VOLUME_3PAR_NAME, new_settings)
        ]
        mock_client.assert_has_calls(expected)
        expected = [
            mock.call.modifyVolume(self.VOLUME_3PAR_NAME, old_settings),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_revert_comment(self, _mock_volume_types):
        _mock_volume_types.side_effect = [
            self.RETYPE_VOLUME_TYPE_2, self.RETYPE_VOLUME_TYPE_1]
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
        mock_client.getVolume.return_value = self.RETYPE_VOLUME_INFO_1

        # Fail the QOS setting to test the revert of the snap CPG rename.
        mock_client.deleteVolumeSet.side_effect = hpexceptions.HTTPForbidden

        self.assertRaises(hpexceptions.HTTPForbidden,
                          self.driver.retype,
                          self.ctxt,
                          {'id': self.VOLUME_ID},
                          self.RETYPE_VOLUME_TYPE_2,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST)

        original = {
            'snapCPG': self.RETYPE_VOLUME_INFO_1['snapCPG'],
            'comment': self.RETYPE_VOLUME_INFO_1['comment']}

        expected = [
            mock.call.modifyVolume('osv-0DM4qZEVSKON-DXN-NwVpw', original),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_different_array(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)

        mock_client.getStorageSystemInfo.return_value = {
            'serialNumber': 'XXXXXXX'}

        self.assertRaises(exception.InvalidHost,
                          self.driver.retype,
                          self.ctxt,
                          self.RETYPE_VOLUME_INFO_0,
                          self.RETYPE_VOLUME_TYPE_1,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.getStorageSystemInfo(),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_across_cpg_domains(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)

        mock_client.getCPG.side_effect = [
            {'domain': 'domain1'},
            {'domain': 'domain2'},
        ]

        self.assertRaises(exception.Invalid3PARDomain,
                          self.driver.retype,
                          self.ctxt,
                          self.RETYPE_VOLUME_INFO_0,
                          self.RETYPE_VOLUME_TYPE_1,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.getStorageSystemInfo(),
            mock.call.getCPG(self.RETYPE_VOLUME_INFO_0['userCPG']),
            mock.call.getCPG(self.RETYPE_VOLUME_TYPE_1['extra_specs']['cpg']),
            mock.call.logout()
        ]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_across_snap_cpg_domains(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)

        mock_client.getCPG.side_effect = [
            {'domain': 'cpg_domain'},
            {'domain': 'cpg_domain'},
            {'domain': 'snap_cpg_domain_1'},
        ]

        self.assertRaises(exception.Invalid3PARDomain,
                          self.driver.retype,
                          self.ctxt,
                          self.RETYPE_VOLUME_INFO_0,
                          self.RETYPE_VOLUME_TYPE_1,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.getStorageSystemInfo(),
            mock.call.getCPG(self.RETYPE_VOLUME_INFO_0['userCPG']),
            mock.call.getCPG(self.RETYPE_VOLUME_TYPE_1['extra_specs']['cpg']),
            mock.call.getCPG(
                self.RETYPE_VOLUME_TYPE_1['extra_specs']['snap_cpg']),
            mock.call.logout()
        ]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_to_bad_persona(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_BAD_PERSONA
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)

        self.assertRaises(exception.InvalidInput,
                          self.driver.retype,
                          self.ctxt,
                          self.RETYPE_VOLUME_INFO_0,
                          self.RETYPE_VOLUME_TYPE_BAD_PERSONA,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.logout()
        ]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_to_bad_cpg(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_BAD_CPG
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
        mock_client.getCPG.side_effect = hpexceptions.HTTPNotFound

        self.assertRaises(exception.InvalidInput,
                          self.driver.retype,
                          self.ctxt,
                          self.RETYPE_VOLUME_INFO_0,
                          self.RETYPE_VOLUME_TYPE_BAD_CPG,
                          self.RETYPE_DIFF,
                          self.RETYPE_HOST)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getCPG(
                self.RETYPE_VOLUME_TYPE_BAD_CPG['extra_specs']['cpg']),
            mock.call.logout()
        ]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_tune(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)

        qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS)
        type_ref = volume_types.create(self.ctxt,
                                       "type1", {"qos:maxIOPS": "100",
                                                 "qos:maxBWS": "50",
                                                 "qos:minIOPS": "10",
                                                 "qos:minBWS": "20",
                                                 "qos:latency": "5",
                                                 "qos:priority": "high"})
        qos_specs.associate_qos_with_type(self.ctxt,
                                          qos_ref['id'],
                                          type_ref['id'])

        type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])

        volume = {'id': HP3PARBaseDriver.CLONE_ID}

        self.driver.retype(self.ctxt, volume, type_ref, None, self.RETYPE_HOST)

        expected = [
            mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA',
                                   {'comment': mock.ANY,
                                    'snapCPG': 'OpenStackCPGSnap'}),
            mock.call.deleteVolumeSet('vvs-0DM4qZEVSKON-AAAAAAAAA'),
            mock.call.addVolumeToVolumeSet('myvvs',
                                           'osv-0DM4qZEVSKON-AAAAAAAAA'),
            mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA',
                                   {'action': 6,
                                    'userCPG': 'OpenStackCPG',
                                    'conversionOperation': 1,
                                    'tuneOperation': 1}),
            mock.call.getTask(1),
            mock.call.logout()
        ]
        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_retype_qos_spec(self, _mock_volume_types):
        _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
        mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)

        cpg = "any_cpg"
        snap_cpg = "any_cpg"
        self.driver.common._retype(self.volume,
                                   HP3PARBaseDriver.VOLUME_3PAR_NAME,
                                   "old_type", "old_type_id",
                                   HP3PARBaseDriver.RETYPE_HOST,
                                   None, cpg, cpg, snap_cpg, snap_cpg,
                                   True, True, None, None,
                                   self.QOS_SPECS, self.RETYPE_QOS_SPECS,
                                   "{}")

        expected = [
            mock.call.createVolumeSet('vvs-0DM4qZEVSKON-DXN-NwVpw', None),
            mock.call.createQoSRules(
                'vvs-0DM4qZEVSKON-DXN-NwVpw',
                {'ioMinGoal': 100, 'ioMaxLimit': 1000,
                 'bwMinGoalKB': 25600, 'bwMaxLimitKB': 51200,
                 'priority': 3,
                 'latencyGoal': 25}
            ),
            mock.call.addVolumeToVolumeSet(
                'vvs-0DM4qZEVSKON-DXN-NwVpw', 'osv-0DM4qZEVSKON-DXN-NwVpw')]
        mock_client.assert_has_calls(expected)

    def test_delete_volume(self):

        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        self.driver.delete_volume(self.volume)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.deleteVolume(self.VOLUME_3PAR_NAME),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    def test_create_cloned_volume(self):

        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.copyVolume.return_value = {'taskid': 1}

        volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
                  'id': HP3PARBaseDriver.CLONE_ID,
                  'display_name': 'Foo Volume',
                  'size': 2,
                  'host': HP3PARBaseDriver.FAKE_HOST,
                  'source_volid': HP3PARBaseDriver.VOLUME_ID}
        src_vref = {}
        model_update = self.driver.create_cloned_volume(volume, src_vref)
        self.assertIsNotNone(model_update)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.copyVolume(
                self.VOLUME_3PAR_NAME,
                'osv-0DM4qZEVSKON-AAAAAAAAA',
                HP3PAR_CPG,
                {'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
                 'online': True}),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    def test_migrate_volume(self):

        conf = {
            'getStorageSystemInfo.return_value': {
                'serialNumber': '1234'},
            'getTask.return_value': {
                'status': 1},
            'getCPG.return_value': {},
            'copyVolume.return_value': {'taskid': 1},
            'getVolume.return_value': {}
        }

        mock_client = self.setup_driver(mock_conf=conf)

        volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
                  'id': HP3PARBaseDriver.CLONE_ID,
                  'display_name': 'Foo Volume',
                  'size': 2,
                  'status': 'available',
                  'host': HP3PARBaseDriver.FAKE_HOST,
                  'source_volid': HP3PARBaseDriver.VOLUME_ID}

        volume_name_3par = self.driver.common._encode_name(volume['id'])

        loc_info = 'HP3PARDriver:1234:CPG-FC1'
        host = {'host': 'stack@3parfc1',
                'capabilities': {'location_info': loc_info}}

        result = self.driver.migrate_volume(context.get_admin_context(),
                                            volume, host)
        self.assertIsNotNone(result)
        self.assertEqual((True, None), result)

        osv_matcher = 'osv-' + volume_name_3par
        omv_matcher = 'omv-' + volume_name_3par

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getStorageSystemInfo(),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getCPG('CPG-FC1'),
            mock.call.copyVolume(osv_matcher, omv_matcher, mock.ANY, mock.ANY),
            mock.call.getTask(mock.ANY),
            mock.call.getVolume(osv_matcher),
            mock.call.deleteVolume(osv_matcher),
            mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)

    def test_migrate_volume_diff_host(self):
        conf = {
            'getStorageSystemInfo.return_value': {
                'serialNumber': 'different'},
        }

        self.setup_driver(mock_conf=conf)

        volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
                  'id': HP3PARBaseDriver.CLONE_ID,
                  'display_name': 'Foo Volume',
                  'size': 2,
                  'status': 'available',
                  'host': HP3PARBaseDriver.FAKE_HOST,
                  'source_volid': HP3PARBaseDriver.VOLUME_ID}

        loc_info = 'HP3PARDriver:1234:CPG-FC1'
        host = {'host': 'stack@3parfc1',
                'capabilities': {'location_info': loc_info}}

        result = self.driver.migrate_volume(context.get_admin_context(),
                                            volume, host)
        self.assertIsNotNone(result)
        self.assertEqual((False, None), result)

    def test_migrate_volume_diff_domain(self):
        conf = {
            'getStorageSystemInfo.return_value': {
                'serialNumber': '1234'},
            'getTask.return_value': {
                'status': 1},
            'getCPG.side_effect':
            lambda x: {'OpenStackCPG': {'domain': 'OpenStack'}}.get(x, {})
        }

        self.setup_driver(mock_conf=conf)

        volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
                  'id': HP3PARBaseDriver.CLONE_ID,
                  'display_name': 'Foo Volume',
                  'size': 2,
                  'status': 'available',
                  'host': HP3PARBaseDriver.FAKE_HOST,
                  'source_volid': HP3PARBaseDriver.VOLUME_ID}

        loc_info = 'HP3PARDriver:1234:CPG-FC1'
        host = {'host': 'stack@3parfc1',
                'capabilities': {'location_info': loc_info}}

        result = self.driver.migrate_volume(context.get_admin_context(),
                                            volume, host)
        self.assertIsNotNone(result)
        self.assertEqual((False, None), result)

    def test_migrate_volume_attached(self):

        mock_client = self.setup_driver()

        volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
                  'id': HP3PARBaseDriver.CLONE_ID,
                  'display_name': 'Foo Volume',
                  'size': 2,
                  'status': 'in-use',
                  'host': HP3PARBaseDriver.FAKE_HOST,
                  'source_volid': HP3PARBaseDriver.VOLUME_ID}

        volume_name_3par = self.driver.common._encode_name(volume['id'])

        mock_client.getVLUNs.return_value = {
            'members': [{'volumeName': 'osv-' + volume_name_3par}]}

        loc_info = 'HP3PARDriver:1234:CPG-FC1'
        host = {'host': 'stack@3parfc1',
                'capabilities': {'location_info': loc_info}}

        result = self.driver.migrate_volume(context.get_admin_context(),
                                            volume, host)
        self.assertIsNotNone(result)
        self.assertEqual((False, None), result)

    def test_attach_volume(self):

        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        self.driver.attach_volume(context.get_admin_context(),
                                  self.volume,
                                  'abcdef',
                                  'newhost',
                                  '/dev/vdb')

        expected = [
            mock.call.setVolumeMetaData(
                self.VOLUME_3PAR_NAME,
                'HPQ-CS-instance_uuid',
                'abcdef')]

        mock_client.assert_has_calls(expected)

        # test the exception
        mock_client.setVolumeMetaData.side_effect = Exception('Custom ex')
        self.assertRaises(exception.CinderException,
                          self.driver.attach_volume,
                          context.get_admin_context(),
                          self.volume,
                          'abcdef',
                          'newhost',
                          '/dev/vdb')

    def test_detach_volume(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        self.driver.detach_volume(context.get_admin_context(), self.volume)
        expected = [
            mock.call.removeVolumeMetaData(
                self.VOLUME_3PAR_NAME,
                'HPQ-CS-instance_uuid')]

        mock_client.assert_has_calls(expected)

        # test the exception
        mock_client.removeVolumeMetaData.side_effect = Exception('Custom ex')
        self.assertRaises(exception.CinderException,
                          self.driver.detach_volume,
                          context.get_admin_context(),
                          self.volume)

    def test_create_snapshot(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        self.driver.create_snapshot(self.snapshot)

        commet = (
            '{"volume_id": "761fc5e5-5191-4ec7-aeba-33e36de44156",'
            ' "display_name": "fakesnap",'
            ' "description": "test description name",'
            ' "volume_name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.createSnapshot(
                'oss-L4I73ONuTci9Fd4ceij-MQ',
                'osv-dh-F5VGRTseuujPjbeRBVg',
                {
                    'comment': commet,
                    'readOnly': True}),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    def test_delete_snapshot(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        self.driver.delete_snapshot(self.snapshot)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.deleteVolume('oss-L4I73ONuTci9Fd4ceij-MQ'),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    def test_delete_snapshot_in_use(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        self.driver.create_snapshot(self.snapshot)
        self.driver.create_volume_from_snapshot(self.volume, self.snapshot)

        ex = hpexceptions.HTTPConflict("In use")
        mock_client.deleteVolume = mock.Mock(side_effect=ex)

        # Deleting the snapshot that a volume is dependent on should fail
        self.assertRaises(exception.SnapshotIsBusy,
                          self.driver.delete_snapshot,
                          self.snapshot)

    def test_delete_snapshot_not_found(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        self.driver.create_snapshot(self.snapshot)

        try:
            ex = hpexceptions.HTTPNotFound("not found")
            mock_client.deleteVolume = mock.Mock(side_effect=ex)
            self.driver.delete_snapshot(self.snapshot)
        except Exception:
            self.fail("Deleting a snapshot that is missing should act as if "
                      "it worked.")

    def test_create_volume_from_snapshot(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        self.driver.create_volume_from_snapshot(self.volume, self.snapshot)

        comment = (
            '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
            ' "display_name": "Foo Volume",'
            ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.createSnapshot(
                self.VOLUME_3PAR_NAME,
                'oss-L4I73ONuTci9Fd4ceij-MQ',
                {
                    'comment': comment,
                    'readOnly': False}),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

        volume = self.volume.copy()
        volume['size'] = 1
        self.assertRaises(exception.InvalidInput,
                          self.driver.create_volume_from_snapshot,
                          volume, self.snapshot)

    def test_create_volume_from_snapshot_and_extend(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        conf = {
            'getTask.return_value': {
                'status': 1},
            'copyVolume.return_value': {'taskid': 1},
            'getVolume.return_value': {}
        }

        mock_client = self.setup_driver(mock_conf=conf)

        volume = self.volume.copy()
        volume['size'] = self.volume['size'] + 10
        self.driver.create_volume_from_snapshot(volume, self.snapshot)

        comment = (
            '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
            ' "display_name": "Foo Volume",'
            ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')

        volume_name_3par = self.driver.common._encode_name(volume['id'])
        osv_matcher = 'osv-' + volume_name_3par
        omv_matcher = 'omv-' + volume_name_3par

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.createSnapshot(
                self.VOLUME_3PAR_NAME,
                'oss-L4I73ONuTci9Fd4ceij-MQ',
                {
                    'comment': comment,
                    'readOnly': False}),
            mock.call.copyVolume(osv_matcher, omv_matcher, mock.ANY, mock.ANY),
            mock.call.getTask(mock.ANY),
            mock.call.getVolume(osv_matcher),
            mock.call.deleteVolume(osv_matcher),
            mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
            mock.call.growVolume(osv_matcher, 10 * 1024),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    def test_create_volume_from_snapshot_and_extend_copy_fail(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        conf = {
            'getTask.return_value': {
                'status': 4,
                'failure message': 'out of disk space'},
            'copyVolume.return_value': {'taskid': 1},
            'getVolume.return_value': {}
        }

        self.setup_driver(mock_conf=conf)

        volume = self.volume.copy()
        volume['size'] = self.volume['size'] + 10

        self.assertRaises(exception.CinderException,
                          self.driver.create_volume_from_snapshot,
                          volume, self.snapshot)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_create_volume_from_snapshot_qos(self, _mock_volume_types):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        _mock_volume_types.return_value = {
            'name': 'gold',
            'extra_specs': {
                'cpg': HP3PAR_CPG,
                'snap_cpg': HP3PAR_CPG_SNAP,
                'vvs_name': self.VVS_NAME,
                'qos': self.QOS,
                'tpvv': True,
                'volume_type': self.volume_type}}
        self.driver.create_volume_from_snapshot(self.volume_qos, self.snapshot)

        comment = (
            '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
            ' "display_name": "Foo Volume",'
            ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.createSnapshot(
                self.VOLUME_3PAR_NAME,
                'oss-L4I73ONuTci9Fd4ceij-MQ', {
                    'comment': comment,
                    'readOnly': False}),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

        volume = self.volume.copy()
        volume['size'] = 1
        self.assertRaises(exception.InvalidInput,
                          self.driver.create_volume_from_snapshot,
                          volume, self.snapshot)

    def test_terminate_connection(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getHostVLUNs.return_value = [
            {'active': True,
             'volumeName': self.VOLUME_3PAR_NAME,
             'lun': None, 'type': 0}]

        self.driver.terminate_connection(
            self.volume,
            self.connector,
            force=True)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.deleteVLUN(
                self.VOLUME_3PAR_NAME,
                None,
                self.FAKE_HOST),
            mock.call.deleteHost(self.FAKE_HOST),
            mock.call.removeVolumeMetaData(
                self.VOLUME_3PAR_NAME, CHAP_USER_KEY),
            mock.call.removeVolumeMetaData(
                self.VOLUME_3PAR_NAME, CHAP_PASS_KEY),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    def test_update_volume_key_value_pair(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        key = 'a'
        value = 'b'
        self.driver.common.update_volume_key_value_pair(
            self.volume,
            key,
            value)

        expected = [
            mock.call.setVolumeMetaData(self.VOLUME_3PAR_NAME, key, value)]

        mock_client.assert_has_calls(expected)

        # check exception
        mock_client.setVolumeMetaData.side_effect = Exception('fake')
        self.assertRaises(exception.VolumeBackendAPIException,
                          self.driver.common.update_volume_key_value_pair,
                          self.volume,
                          None,
                          'b')

    def test_clear_volume_key_value_pair(self):

        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        key = 'a'
        self.driver.common.clear_volume_key_value_pair(self.volume, key)

        expected = [
            mock.call.removeVolumeMetaData(self.VOLUME_3PAR_NAME, key)]

        mock_client.assert_has_calls(expected)

        # check the exception
        mock_client.removeVolumeMetaData.side_effect = Exception('fake')
        self.assertRaises(exception.VolumeBackendAPIException,
                          self.driver.common.clear_volume_key_value_pair,
                          self.volume,
                          None)

    def test_extend_volume(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        grow_size = 3
        old_size = self.volume['size']
        new_size = old_size + grow_size
        self.driver.extend_volume(self.volume, str(new_size))
        growth_size_mib = grow_size * units.Ki

        expected = [
            mock.call.growVolume(self.VOLUME_3PAR_NAME, growth_size_mib)]

        mock_client.assert_has_calls(expected)

    def test_extend_volume_non_base(self):
        extend_ex = hpexceptions.HTTPForbidden(error={'code': 150})
        conf = {
            'getTask.return_value': {
                'status': 1},
            'getCPG.return_value': {},
            'copyVolume.return_value': {'taskid': 1},
            'getVolume.return_value': {},
            # Throw an exception first time only
            'growVolume.side_effect': [extend_ex,
                                       None],
        }

        mock_client = self.setup_driver(mock_conf=conf)
        grow_size = 3
        old_size = self.volume['size']
        new_size = old_size + grow_size
        self.driver.extend_volume(self.volume, str(new_size))

        self.assertEqual(2, mock_client.growVolume.call_count)

    def test_extend_volume_non_base_failure(self):
        extend_ex = hpexceptions.HTTPForbidden(error={'code': 150})
        conf = {
            'getTask.return_value': {
                'status': 1},
            'getCPG.return_value': {},
            'copyVolume.return_value': {'taskid': 1},
            'getVolume.return_value': {},
            # Always fail
            'growVolume.side_effect': extend_ex
        }

        self.setup_driver(mock_conf=conf)
        grow_size = 3
        old_size = self.volume['size']
        new_size = old_size + grow_size
        self.assertRaises(hpexceptions.HTTPForbidden,
                          self.driver.extend_volume,
                          self.volume,
                          str(new_size))

    def test_get_ports(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getPorts.return_value = {
            'members': [
                {'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
                 'protocol': 2,
                 'IPAddr': '10.10.120.252',
                 'linkState': 4,
                 'device': [],
                 'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
                 'mode': 2,
                 'HWAddr': '2C27D75375D2',
                 'type': 8},
                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
                 'protocol': 2,
                 'IPAddr': '10.10.220.253',
                 'linkState': 4,
                 'device': [],
                 'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
                 'mode': 2,
                 'HWAddr': '2C27D75375D6',
                 'type': 8},
                {'portWWN': '20210002AC00383D',
                 'protocol': 1,
                 'linkState': 4,
                 'mode': 2,
                 'device': ['cage2'],
                 'nodeWWN': '20210002AC00383D',
                 'type': 2,
                 'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]}

        ports = self.driver.common.get_ports()['members']
        self.assertEqual(len(ports), 3)

    def test_get_by_qos_spec_with_scoping(self):
        self.setup_driver()
        qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS)
        type_ref = volume_types.create(self.ctxt,
                                       "type1", {"qos:maxIOPS": "100",
                                                 "qos:maxBWS": "50",
                                                 "qos:minIOPS": "10",
                                                 "qos:minBWS": "20",
                                                 "qos:latency": "5",
                                                 "qos:priority": "high"})
        qos_specs.associate_qos_with_type(self.ctxt,
                                          qos_ref['id'],
                                          type_ref['id'])
        type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
        qos = self.driver.common._get_qos_by_volume_type(type_ref)
        self.assertEqual(qos, {'maxIOPS': '1000', 'maxBWS': '50',
                               'minIOPS': '100', 'minBWS': '25',
                               'latency': '25', 'priority': 'low'})

    def test_get_by_qos_spec(self):
        self.setup_driver()
        qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS_SPECS)
        type_ref = volume_types.create(self.ctxt,
                                       "type1", {"qos:maxIOPS": "100",
                                                 "qos:maxBWS": "50",
                                                 "qos:minIOPS": "10",
                                                 "qos:minBWS": "20",
                                                 "qos:latency": "5",
                                                 "qos:priority": "high"})
        qos_specs.associate_qos_with_type(self.ctxt,
                                          qos_ref['id'],
                                          type_ref['id'])
        type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
        qos = self.driver.common._get_qos_by_volume_type(type_ref)
        self.assertEqual(qos, {'maxIOPS': '1000', 'maxBWS': '50',
                               'minIOPS': '100', 'minBWS': '25',
                               'latency': '25', 'priority': 'low'})

    def test_get_by_qos_by_type_only(self):
        self.setup_driver()
        type_ref = volume_types.create(self.ctxt,
                                       "type1", {"qos:maxIOPS": "100",
                                                 "qos:maxBWS": "50",
                                                 "qos:minIOPS": "10",
                                                 "qos:minBWS": "20",
                                                 "qos:latency": "5",
                                                 "qos:priority": "high"})
        type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
        qos = self.driver.common._get_qos_by_volume_type(type_ref)
        self.assertEqual(qos, {'maxIOPS': '100', 'maxBWS': '50',
                               'minIOPS': '10', 'minBWS': '20',
                               'latency': '5', 'priority': 'high'})

    def test_create_vlun(self):
        host = 'fake-host'
        lun_id = 11
        nsp = '1:2:3'
        mock_client = self.setup_driver()
        location = ("%(name)s,%(lunid)s,%(host)s,%(nsp)s" %
                    {'name': self.VOLUME_NAME,
                     'lunid': lun_id,
                     'host': host,
                     'nsp': nsp})
        mock_client.createVLUN.return_value = location

        expected_info = {'volume_name': self.VOLUME_NAME,
                         'lun_id': lun_id,
                         'host_name': host,
                         'nsp': nsp}
        vlun_info = self.driver.common._create_3par_vlun(self.VOLUME_NAME,
                                                         host, nsp)
        self.assertEqual(expected_info, vlun_info)

        location = ("%(name)s,%(lunid)s,%(host)s" %
                    {'name': self.VOLUME_NAME,
                     'lunid': lun_id,
                     'host': host})
        mock_client.createVLUN.return_value = location
        expected_info = {'volume_name': self.VOLUME_NAME,
                         'lun_id': lun_id,
                         'host_name': host}
        vlun_info = self.driver.common._create_3par_vlun(self.VOLUME_NAME,
                                                         host, None)
        self.assertEqual(expected_info, vlun_info)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_manage_existing(self, _mock_volume_types):
        _mock_volume_types.return_value = self.volume_type
        mock_client = self.setup_driver()

        new_comment = {"display_name": "Foo Volume",
                       "name": "volume-007dbfce-7579-40bc-8f90-a20b3902283e",
                       "volume_id": "007dbfce-7579-40bc-8f90-a20b3902283e",
                       "type": "OpenStack"}

        volume = {'display_name': None,
                  'volume_type': 'gold',
                  'volume_type_id': 'acfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
                  'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}

        mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
        mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
        mock_client.getTask.return_value = self.STATUS_DONE

        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])
        osv_matcher = self.driver.common._get_3par_vol_name(volume['id'])
        vvs_matcher = self.driver.common._get_3par_vvs_name(volume['id'])
        existing_ref = {'source-name': unm_matcher}

        obj = self.driver.manage_existing(volume, existing_ref)

        expected_obj = {'display_name': 'Foo Volume'}

        expected_manage = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.modifyVolume(existing_ref['source-name'],
                                   {'newName': osv_matcher,
                                    'comment': self.CommentMatcher(
                                        self.assertEqual, new_comment)}),
        ]

        retype_comment_qos = {
            "display_name": "Foo Volume",
            "volume_type_name": self.volume_type['name'],
            "volume_type_id": self.volume_type['id'],
            "qos": {
                'maxIOPS': '1000',
                'maxBWS': '50',
                'minIOPS': '100',
                'minBWS': '25',
                'latency': '25',
                'priority': 'low'
            }
        }

        expected_retype_modify = [
            mock.call.modifyVolume(osv_matcher,
                                   {'comment': self.CommentMatcher(
                                       self.assertEqual, retype_comment_qos),
                                    'snapCPG': 'OpenStackCPGSnap'}),
            mock.call.deleteVolumeSet(vvs_matcher),
        ]

        expected_retype_specs = [
            mock.call.createVolumeSet(vvs_matcher, None),
            mock.call.createQoSRules(
                vvs_matcher,
                {'ioMinGoal': 100, 'ioMaxLimit': 1000,
                 'bwMinGoalKB': 25600, 'priority': 1, 'latencyGoal': 25,
                 'bwMaxLimitKB': 51200}),
            mock.call.addVolumeToVolumeSet(vvs_matcher, osv_matcher),
            mock.call.modifyVolume(osv_matcher,
                                   {'action': 6, 'userCPG': 'OpenStackCPG',
                                    'conversionOperation': 1,
                                    'tuneOperation': 1}),
            mock.call.getTask(1),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected_manage)
        mock_client.assert_has_calls(expected_retype_modify)
        mock_client.assert_has_calls(expected_retype_specs)
        self.assertEqual(expected_obj, obj)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_manage_existing_vvs(self, _mock_volume_types):
        test_volume_type = self.RETYPE_VOLUME_TYPE_2
        vvs = test_volume_type['extra_specs']['vvs']
        _mock_volume_types.return_value = test_volume_type
        mock_client = self.setup_driver()

        mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
        mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
        mock_client.getTask.return_value = self.STATUS_DONE

        id = '007abcde-7579-40bc-8f90-a20b3902283e'
        new_comment = {"display_name": "Test Volume",
                       "name": ("volume-%s" % id),
                       "volume_id": id,
                       "type": "OpenStack"}

        volume = {'display_name': 'Test Volume',
                  'volume_type': 'gold',
                  'volume_type_id': 'acfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
                  'id': id}

        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])
        osv_matcher = self.driver.common._get_3par_vol_name(volume['id'])
        vvs_matcher = self.driver.common._get_3par_vvs_name(volume['id'])

        existing_ref = {'source-name': unm_matcher}

        obj = self.driver.manage_existing(volume, existing_ref)

        expected_obj = {'display_name': 'Test Volume'}
        expected_manage = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.modifyVolume(existing_ref['source-name'],
                                   {'newName': osv_matcher,
                                    'comment': self.CommentMatcher(
                                        self.assertEqual, new_comment)})
        ]

        retype_comment_vvs = {
            "display_name": "Foo Volume",
            "volume_type_name": test_volume_type['name'],
            "volume_type_id": test_volume_type['id'],
            "vvs": vvs
        }

        expected_retype = [
            mock.call.modifyVolume(osv_matcher,
                                   {'comment': self.CommentMatcher(
                                       self.assertEqual, retype_comment_vvs),
                                    'snapCPG': 'OpenStackCPGSnap'}),
            mock.call.deleteVolumeSet(vvs_matcher),
            mock.call.addVolumeToVolumeSet(vvs, osv_matcher),
            mock.call.modifyVolume(osv_matcher,
                                   {'action': 6, 'userCPG': 'OpenStackCPG',
                                    'conversionOperation': 1,
                                    'tuneOperation': 1}),
            mock.call.getTask(1),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected_manage)
        mock_client.assert_has_calls(expected_retype)
        self.assertEqual(expected_obj, obj)

    def test_manage_existing_no_volume_type(self):
        mock_client = self.setup_driver()

        comment = (
            '{"display_name": "Foo Volume"}')
        new_comment = (
            '{"type": "OpenStack",'
            ' "display_name": "Foo Volume",'
            ' "name": "volume-007dbfce-7579-40bc-8f90-a20b3902283e",'
            ' "volume_id": "007dbfce-7579-40bc-8f90-a20b3902283e"}')
        volume = {'display_name': None,
                  'volume_type': None,
                  'volume_type_id': None,
                  'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}

        mock_client.getVolume.return_value = {'comment': comment}

        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])
        osv_matcher = self.driver.common._get_3par_vol_name(volume['id'])
        existing_ref = {'source-name': unm_matcher}

        obj = self.driver.manage_existing(volume, existing_ref)

        expected_obj = {'display_name': 'Foo Volume'}
        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.modifyVolume(existing_ref['source-name'],
                                   {'newName': osv_matcher,
                                    'comment': new_comment}),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)
        self.assertEqual(expected_obj, obj)

        volume['display_name'] = 'Test Volume'

        obj = self.driver.manage_existing(volume, existing_ref)

        expected_obj = {'display_name': 'Test Volume'}
        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.modifyVolume(existing_ref['source-name'],
                                   {'newName': osv_matcher,
                                    'comment': new_comment}),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)
        self.assertEqual(expected_obj, obj)

        mock_client.getVolume.return_value = {}
        volume['display_name'] = None

        obj = self.driver.manage_existing(volume, existing_ref)

        expected_obj = {'display_name': None}
        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.modifyVolume(existing_ref['source-name'],
                                   {'newName': osv_matcher,
                                    'comment': new_comment}),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)
        self.assertEqual(expected_obj, obj)

    def test_manage_existing_invalid_input(self):
        mock_client = self.setup_driver()

        volume = {'display_name': None,
                  'volume_type': None,
                  'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}

        mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound('fake')

        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])
        existing_ref = {'source-name': unm_matcher}

        self.assertRaises(exception.InvalidInput,
                          self.driver.manage_existing,
                          volume=volume,
                          existing_ref=existing_ref)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)

    def test_manage_existing_volume_type_exception(self):
        mock_client = self.setup_driver()

        comment = (
            '{"display_name": "Foo Volume"}')
        volume = {'display_name': None,
                  'volume_type': 'gold',
                  'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
                  'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}

        mock_client.getVolume.return_value = {'comment': comment}

        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])
        existing_ref = {'source-name': unm_matcher}

        self.assertRaises(exception.ManageExistingVolumeTypeMismatch,
                          self.driver.manage_existing,
                          volume=volume,
                          existing_ref=existing_ref)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)

    @mock.patch.object(volume_types, 'get_volume_type')
    def test_manage_existing_retype_exception(self, _mock_volume_types):
        mock_client = self.setup_driver()
        _mock_volume_types.return_value = {
            'name': 'gold',
            'id': 'gold-id',
            'extra_specs': {
                'cpg': HP3PAR_CPG,
                'snap_cpg': HP3PAR_CPG_SNAP,
                'vvs_name': self.VVS_NAME,
                'qos': self.QOS,
                'tpvv': True,
                'volume_type': self.volume_type}}

        volume = {'display_name': None,
                  'volume_type': 'gold',
                  'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
                  'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}

        mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
        mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
        mock_client.getTask.return_value = self.STATUS_DONE
        mock_client.getCPG.side_effect = [
            {'domain': 'domain1'},
            {'domain': 'domain2'}
        ]

        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])
        osv_matcher = self.driver.common._get_3par_vol_name(volume['id'])

        existing_ref = {'source-name': unm_matcher}

        self.assertRaises(exception.Invalid3PARDomain,
                          self.driver.manage_existing,
                          volume=volume,
                          existing_ref=existing_ref)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(unm_matcher),
            mock.call.modifyVolume(
                unm_matcher, {'newName': osv_matcher, 'comment': mock.ANY}),
            mock.call.getVolume(osv_matcher),
            mock.call.getCPG('testUserCpg0'),
            mock.call.getCPG('OpenStackCPG'),
            mock.call.modifyVolume(
                osv_matcher, {'newName': unm_matcher,
                              'comment': self.MANAGE_VOLUME_INFO['comment']}),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)

    def test_manage_existing_get_size(self):
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'sizeMiB': 2048}

        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])
        volume = {}
        existing_ref = {'source-name': unm_matcher}

        size = self.driver.manage_existing_get_size(volume, existing_ref)

        expected_size = 2
        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected, True)
        self.assertEqual(expected_size, size)

    def test_manage_existing_get_size_invalid_reference(self):
        mock_client = self.setup_driver()
        volume = {}
        existing_ref = {'source-name': self.VOLUME_3PAR_NAME}

        self.assertRaises(exception.ManageExistingInvalidReference,
                          self.driver.manage_existing_get_size,
                          volume=volume,
                          existing_ref=existing_ref)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)

        existing_ref = {}

        self.assertRaises(exception.ManageExistingInvalidReference,
                          self.driver.manage_existing_get_size,
                          volume=volume,
                          existing_ref=existing_ref)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)

    def test_manage_existing_get_size_invalid_input(self):
        mock_client = self.setup_driver()
        mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound('fake')

        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])
        volume = {}
        existing_ref = {'source-name': unm_matcher}

        self.assertRaises(exception.InvalidInput,
                          self.driver.manage_existing_get_size,
                          volume=volume,
                          existing_ref=existing_ref)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(existing_ref['source-name']),
            mock.call.logout()
        ]

        mock_client.assert_has_calls(expected)

    def test_unmanage(self):
        mock_client = self.setup_driver()

        self.driver.unmanage(self.volume)

        osv_matcher = self.driver.common._get_3par_vol_name(self.volume['id'])
        unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id'])

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.modifyVolume(osv_matcher, {'newName': unm_matcher}),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

    def test__safe_hostname(self):
        long_hostname = "abc123abc123abc123abc123abc123abc123"
        fixed_hostname = "abc123abc123abc123abc123abc123a"
        common = hpcommon.HP3PARCommon(None)
        safe_host = common._safe_hostname(long_hostname)
        self.assertEqual(fixed_hostname, safe_host)


class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):

    properties = {
        'driver_volume_type': 'fibre_channel',
        'data': {
            'target_lun': 90,
            'target_wwn': ['0987654321234', '123456789000987'],
            'target_discovered': True,
            'initiator_target_map': {'123456789012345':
                                     ['0987654321234', '123456789000987'],
                                     '123456789054321':
                                     ['0987654321234', '123456789000987'],
                                     }}}

    def setup_driver(self, config=None, mock_conf=None):

        self.ctxt = context.get_admin_context()
        mock_client = self.setup_mock_client(
            conf=config,
            m_conf=mock_conf,
            driver=hpfcdriver.HP3PARFCDriver)

        expected = [
            mock.call.setSSHOptions(
                HP3PAR_SAN_IP,
                HP3PAR_USER_NAME,
                HP3PAR_USER_PASS,
                privatekey=HP3PAR_SAN_SSH_PRIVATE,
                port=HP3PAR_SAN_SSH_PORT,
                conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)
        mock_client.reset_mock()
        return mock_client

    def test_initialize_connection(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('fake'),
            {'name': self.FAKE_HOST,
                'FCPaths': [{'driverVersion': None,
                             'firmwareVersion': None,
                             'hostSpeed': 0,
                             'model': None,
                             'portPos': {'cardPort': 1, 'node': 1,
                                         'slot': 2},
                             'vendor': None,
                             'wwn': self.wwn[0]},
                            {'driverVersion': None,
                             'firmwareVersion': None,
                             'hostSpeed': 0,
                             'model': None,
                             'portPos': {'cardPort': 1, 'node': 0,
                                         'slot': 2},
                             'vendor': None,
                             'wwn': self.wwn[1]}]}]
        mock_client.findHost.return_value = self.FAKE_HOST
        mock_client.getHostVLUNs.return_value = [
            {'active': True,
             'volumeName': self.VOLUME_3PAR_NAME,
             'lun': 90, 'type': 0}]
        location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
                    {'volume_name': self.VOLUME_3PAR_NAME,
                     'lun_id': 90,
                     'host': self.FAKE_HOST,
                     'nsp': 'something'})
        mock_client.createVLUN.return_value = location

        result = self.driver.initialize_connection(self.volume, self.connector)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost(self.FAKE_HOST),
            mock.ANY,
            mock.call.getHost(self.FAKE_HOST),
            mock.call.getPorts(),
            mock.call.createVLUN(
                self.VOLUME_3PAR_NAME,
                auto=True,
                hostname=self.FAKE_HOST),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

        self.assertDictMatch(result, self.properties)

    @mock.patch('cinder.zonemanager.utils.create_lookup_service')
    def test_initialize_connection_with_lookup_single_nsp(self, mock_lookup):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        class fake_lookup_object:
            def get_device_mapping_from_network(self, connector, target_wwns):
                fake_map = {
                    'FAB_1': {
                        'target_port_wwn_list': ['0987654321234'],
                        'initiator_port_wwn_list': ['123456789012345']
                    }
                }
                return fake_map
        mock_lookup.return_value = fake_lookup_object()
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('fake'),
            {'name': self.FAKE_HOST,
                'FCPaths': [{'driverVersion': None,
                             'firmwareVersion': None,
                             'hostSpeed': 0,
                             'model': None,
                             'portPos': {'cardPort': 1, 'node': 1,
                                         'slot': 2},
                             'vendor': None,
                             'wwn': self.wwn[0]}]}]
        mock_client.findHost.return_value = self.FAKE_HOST
        mock_client.getHostVLUNs.return_value = [
            {'active': True,
             'volumeName': self.VOLUME_3PAR_NAME,
             'lun': 90, 'type': 0}]
        location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
                    {'volume_name': self.VOLUME_3PAR_NAME,
                     'lun_id': 90,
                     'host': self.FAKE_HOST,
                     'nsp': 'something'})
        mock_client.createVLUN.return_value = location

        connector = {'ip': '10.0.0.2',
                     'initiator': 'iqn.1993-08.org.debian:01:222',
                     'wwpns': [self.wwn[0]],
                     'wwnns': ["223456789012345"],
                     'host': self.FAKE_HOST}

        expected_properties = {
            'driver_volume_type': 'fibre_channel',
            'data': {
                'target_lun': 90,
                'target_wwn': ['0987654321234'],
                'target_discovered': True,
                'initiator_target_map': {'123456789012345':
                                         ['0987654321234']
                                         }}}

        result = self.driver.initialize_connection(self.volume, connector)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost(self.FAKE_HOST),
            mock.ANY,
            mock.call.getHost(self.FAKE_HOST),
            mock.call.getPorts(),
            mock.call.getPorts(),
            mock.call.createVLUN(
                self.VOLUME_3PAR_NAME,
                auto=True,
                hostname=self.FAKE_HOST,
                portPos={'node': 7, 'slot': 1, 'cardPort': 1}),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

        self.assertDictMatch(result, expected_properties)

    def test_terminate_connection(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        effects = [
            [{'active': True, 'volumeName': self.VOLUME_3PAR_NAME,
              'lun': None, 'type': 0}],
            hpexceptions.HTTPNotFound]

        mock_client.getHostVLUNs.side_effect = effects

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.deleteVLUN(
                self.VOLUME_3PAR_NAME,
                None,
                self.FAKE_HOST),
            mock.call.deleteHost(self.FAKE_HOST),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.getPorts(),
            mock.call.logout()]

        conn_info = self.driver.terminate_connection(self.volume,
                                                     self.connector)
        mock_client.assert_has_calls(expected)
        self.assertIn('data', conn_info)
        self.assertIn('initiator_target_map', conn_info['data'])
        mock_client.reset_mock()

        mock_client.getHostVLUNs.side_effect = effects

        # mock some deleteHost exceptions that are handled
        delete_with_vlun = hpexceptions.HTTPConflict(
            error={'message': "has exported VLUN"})
        delete_with_hostset = hpexceptions.HTTPConflict(
            error={'message': "host is a member of a set"})
        mock_client.deleteHost = mock.Mock(
            side_effect=[delete_with_vlun, delete_with_hostset])

        conn_info = self.driver.terminate_connection(self.volume,
                                                     self.connector)
        mock_client.assert_has_calls(expected)
        mock_client.reset_mock()
        mock_client.getHostVLUNs.side_effect = effects

        conn_info = self.driver.terminate_connection(self.volume,
                                                     self.connector)
        mock_client.assert_has_calls(expected)

    @mock.patch('cinder.zonemanager.utils.create_lookup_service')
    def test_terminate_connection_with_lookup(self, mock_lookup):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        class fake_lookup_object:
            def get_device_mapping_from_network(self, connector, target_wwns):
                fake_map = {
                    'FAB_1': {
                        'target_port_wwn_list': ['0987654321234'],
                        'initiator_port_wwn_list': ['123456789012345']
                    }
                }
                return fake_map
        mock_lookup.return_value = fake_lookup_object()
        mock_client = self.setup_driver()

        effects = [
            [{'active': True, 'volumeName': self.VOLUME_3PAR_NAME,
              'lun': None, 'type': 0}],
            hpexceptions.HTTPNotFound]

        mock_client.getHostVLUNs.side_effect = effects

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.deleteVLUN(
                self.VOLUME_3PAR_NAME,
                None,
                self.FAKE_HOST),
            mock.call.deleteHost(self.FAKE_HOST),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.getPorts(),
            mock.call.logout()]

        conn_info = self.driver.terminate_connection(self.volume,
                                                     self.connector)
        mock_client.assert_has_calls(expected)
        self.assertIn('data', conn_info)
        self.assertIn('initiator_target_map', conn_info['data'])
        mock_client.reset_mock()

        mock_client.getHostVLUNs.side_effect = effects

        # mock some deleteHost exceptions that are handled
        delete_with_vlun = hpexceptions.HTTPConflict(
            error={'message': "has exported VLUN"})
        delete_with_hostset = hpexceptions.HTTPConflict(
            error={'message': "host is a member of a set"})
        mock_client.deleteHost = mock.Mock(
            side_effect=[delete_with_vlun, delete_with_hostset])

        conn_info = self.driver.terminate_connection(self.volume,
                                                     self.connector)
        mock_client.assert_has_calls(expected)
        mock_client.reset_mock()
        mock_client.getHostVLUNs.side_effect = effects

        conn_info = self.driver.terminate_connection(self.volume,
                                                     self.connector)
        mock_client.assert_has_calls(expected)

    def test_terminate_connection_more_vols(self):
        mock_client = self.setup_driver()
        # mock more than one vlun on the host (don't even try to remove host)
        mock_client.getHostVLUNs.return_value = \
            [
                {'active': True,
                 'volumeName': self.VOLUME_3PAR_NAME,
                 'lun': None, 'type': 0},
                {'active': True,
                 'volumeName': 'there-is-another-volume',
                 'lun': None, 'type': 0},
            ]

        expect_less = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.deleteVLUN(
                self.VOLUME_3PAR_NAME,
                None,
                self.FAKE_HOST),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.logout()]

        conn_info = self.driver.terminate_connection(self.volume,
                                                     self.connector)
        mock_client.assert_has_calls(expect_less)
        self.assertNotIn('initiator_target_map', conn_info['data'])

    def test_get_volume_stats(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getCPG.return_value = self.cpgs[0]
        totalCapacityMiB = 8000
        freeCapacityMiB = 4000
        mock_client.getStorageSystemInfo.return_value = {
            'serialNumber': '1234',
            'totalCapacityMiB': totalCapacityMiB,
            'freeCapacityMiB': freeCapacityMiB
        }
        stats = self.driver.get_volume_stats(True)
        const = 0.0009765625
        self.assertEqual(stats['storage_protocol'], 'FC')
        self.assertEqual(stats['total_capacity_gb'], totalCapacityMiB * const)
        self.assertEqual(stats['free_capacity_gb'], freeCapacityMiB * const)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getStorageSystemInfo(),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)
        stats = self.driver.get_volume_stats(True)
        self.assertEqual(stats['storage_protocol'], 'FC')
        self.assertEqual(stats['total_capacity_gb'], totalCapacityMiB * const)
        self.assertEqual(stats['free_capacity_gb'], freeCapacityMiB * const)

        cpg2 = self.cpgs[0].copy()
        cpg2.update({'SDGrowth': {'limitMiB': 8192}})
        mock_client.getCPG.return_value = cpg2

        stats = self.driver.get_volume_stats(True)
        self.assertEqual(stats['storage_protocol'], 'FC')
        total_capacity_gb = 8192 * const
        self.assertEqual(stats['total_capacity_gb'], total_capacity_gb)
        free_capacity_gb = int(
            (8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const)
        self.assertEqual(stats['free_capacity_gb'], free_capacity_gb)
        self.driver.common.client.deleteCPG(HP3PAR_CPG)
        self.driver.common.client.createCPG(HP3PAR_CPG, {})

    def test_create_host(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('fake'),
            {'name': self.FAKE_HOST,
                'FCPaths': [{'driverVersion': None,
                             'firmwareVersion': None,
                             'hostSpeed': 0,
                             'model': None,
                             'portPos': {'cardPort': 1, 'node': 1,
                                         'slot': 2},
                             'vendor': None,
                             'wwn': self.wwn[0]},
                            {'driverVersion': None,
                             'firmwareVersion': None,
                             'hostSpeed': 0,
                             'model': None,
                             'portPos': {'cardPort': 1, 'node': 0,
                                         'slot': 2},
                             'vendor': None,
                             'wwn': self.wwn[1]}]}]
        mock_client.findHost.return_value = None
        mock_client.getVLUN.return_value = {'lun': 186}

        host = self.driver._create_host(self.volume, self.connector)
        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.findHost(wwn='123456789012345'),
            mock.call.findHost(wwn='123456789054321'),
            mock.call.createHost(
                self.FAKE_HOST,
                FCWwns=['123456789012345', '123456789054321'],
                optional={'domain': None, 'persona': 2}),
            mock.call.getHost(self.FAKE_HOST)]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], self.FAKE_HOST)

    def test_create_invalid_host(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('Host not found.'), {
                'name': 'fakehost.foo',
                'FCPaths': [{'wwn': '123456789012345'}, {
                    'wwn': '123456789054321'}]}]
        mock_client.findHost.return_value = 'fakehost.foo'

        host = self.driver._create_host(self.volume, self.connector)

        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost('fakehost'),
            mock.call.findHost(wwn='123456789012345'),
            mock.call.getHost('fakehost.foo')]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], 'fakehost.foo')

    def test_create_modify_host(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [{
            'name': self.FAKE_HOST, 'FCPaths': []},
            {'name': self.FAKE_HOST,
                'FCPaths': [{'wwn': '123456789012345'}, {
                    'wwn': '123456789054321'}]}]

        host = self.driver._create_host(self.volume, self.connector)
        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost('fakehost'),
            mock.call.modifyHost(
                'fakehost', {
                    'FCWWNs': ['123456789012345', '123456789054321'],
                    'pathOperation': 1}),
            mock.call.getHost('fakehost')]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], self.FAKE_HOST)
        self.assertEqual(len(host['FCPaths']), 2)

    def test_modify_host_with_new_wwn(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        getHost_ret1 = {
            'name': self.FAKE_HOST,
            'FCPaths': [{'wwn': '123456789054321'}]}
        getHost_ret2 = {
            'name': self.FAKE_HOST,
            'FCPaths': [{'wwn': '123456789012345'},
                        {'wwn': '123456789054321'}]}
        mock_client.getHost.side_effect = [getHost_ret1, getHost_ret2]

        host = self.driver._create_host(self.volume, self.connector)

        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost('fakehost'),
            mock.call.modifyHost(
                'fakehost', {
                    'FCWWNs': ['123456789012345'], 'pathOperation': 1}),
            mock.call.getHost('fakehost')]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], self.FAKE_HOST)
        self.assertEqual(len(host['FCPaths']), 2)

    def test_modify_host_with_unknown_wwn_and_new_wwn(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        getHost_ret1 = {
            'name': self.FAKE_HOST,
            'FCPaths': [{'wwn': '123456789054321'},
                        {'wwn': 'xxxxxxxxxxxxxxx'}]}
        getHost_ret2 = {
            'name': self.FAKE_HOST,
            'FCPaths': [{'wwn': '123456789012345'},
                        {'wwn': '123456789054321'},
                        {'wwn': 'xxxxxxxxxxxxxxx'}]}
        mock_client.getHost.side_effect = [getHost_ret1, getHost_ret2]

        host = self.driver._create_host(self.volume, self.connector)

        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost('fakehost'),
            mock.call.modifyHost(
                'fakehost', {
                    'FCWWNs': ['123456789012345'], 'pathOperation': 1}),
            mock.call.getHost('fakehost')]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], self.FAKE_HOST)
        self.assertEqual(len(host['FCPaths']), 3)


class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):

    TARGET_IQN = 'iqn.2000-05.com.3pardata:21810002ac00383d'
    TARGET_LUN = 186

    properties = {
        'driver_volume_type': 'iscsi',
        'data':
        {'target_discovered': True,
            'target_iqn': TARGET_IQN,
            'target_lun': TARGET_LUN,
            'target_portal': '1.1.1.2:1234'}}

    def setup_driver(self, config=None, mock_conf=None):

        self.ctxt = context.get_admin_context()

        mock_client = self.setup_mock_client(
            conf=config,
            m_conf=mock_conf,
            driver=hpdriver.HP3PARISCSIDriver)

        expected = [
            mock.call.setSSHOptions(
                HP3PAR_SAN_IP,
                HP3PAR_USER_NAME,
                HP3PAR_USER_PASS,
                privatekey=HP3PAR_SAN_SSH_PRIVATE,
                port=HP3PAR_SAN_SSH_PORT,
                conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.logout(),
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getPorts(),
            mock.call.logout()]
        mock_client.assert_has_calls(expected)
        mock_client.reset_mock()

        return mock_client

    def test_initialize_connection(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('fake'),
            {'name': self.FAKE_HOST}]
        mock_client.findHost.return_value = self.FAKE_HOST
        mock_client.getHostVLUNs.return_value = [
            {'active': True,
             'volumeName': self.VOLUME_3PAR_NAME,
             'lun': self.TARGET_LUN, 'type': 0}]
        location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
                    {'volume_name': self.VOLUME_3PAR_NAME,
                     'lun_id': self.TARGET_LUN,
                     'host': self.FAKE_HOST,
                     'nsp': 'something'})
        mock_client.createVLUN.return_value = location

        result = self.driver.initialize_connection(self.volume, self.connector)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume(self.VOLUME_3PAR_NAME),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.createVLUN(
                self.VOLUME_3PAR_NAME,
                auto=True,
                hostname='fakehost',
                portPos={'node': 8, 'slot': 1, 'cardPort': 1}),
            mock.call.getHostVLUNs(self.FAKE_HOST),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

        self.assertDictMatch(result, self.properties)

    def test_get_volume_stats(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getCPG.return_value = self.cpgs[0]
        totalCapacityMiB = 8000
        freeCapacityMiB = 4000
        mock_client.getStorageSystemInfo.return_value = {
            'serialNumber': '1234',
            'totalCapacityMiB': totalCapacityMiB,
            'freeCapacityMiB': freeCapacityMiB
        }
        stats = self.driver.get_volume_stats(True)
        const = 0.0009765625
        self.assertEqual(stats['storage_protocol'], 'iSCSI')
        self.assertEqual(stats['total_capacity_gb'], totalCapacityMiB * const)
        self.assertEqual(stats['free_capacity_gb'], freeCapacityMiB * const)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getStorageSystemInfo(),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.logout()]

        mock_client.assert_has_calls(expected)

        cpg2 = self.cpgs[0].copy()
        cpg2.update({'SDGrowth': {'limitMiB': 8192}})
        mock_client.getCPG.return_value = cpg2

        stats = self.driver.get_volume_stats(True)
        self.assertEqual(stats['storage_protocol'], 'iSCSI')
        total_capacity_gb = 8192 * const
        self.assertEqual(stats['total_capacity_gb'], total_capacity_gb)
        free_capacity_gb = int(
            (8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const)
        self.assertEqual(stats['free_capacity_gb'], free_capacity_gb)

    def test_create_host(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('fake'),
            {'name': self.FAKE_HOST}]
        mock_client.findHost.return_value = None
        mock_client.getVLUN.return_value = {'lun': self.TARGET_LUN}

        host, auth_username, auth_password = self.driver._create_host(
            self.volume, self.connector)
        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
            mock.call.createHost(
                self.FAKE_HOST,
                optional={'domain': None, 'persona': 2},
                iscsiNames=['iqn.1993-08.org.debian:01:222']),
            mock.call.getHost(self.FAKE_HOST)]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], self.FAKE_HOST)
        self.assertEqual(auth_username, None)
        self.assertEqual(auth_password, None)

    def test_create_host_chap_enabled(self):
        # setup_mock_client drive with CHAP enabled configuration
        # and return the mock HTTP 3PAR client
        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True
        mock_client = self.setup_driver(config=config)

        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('fake'),
            {'name': self.FAKE_HOST}]
        mock_client.findHost.return_value = None
        mock_client.getVLUN.return_value = {'lun': self.TARGET_LUN}

        expected_mod_request = {
            'chapOperation': mock_client.HOST_EDIT_ADD,
            'chapOperationMode': mock_client.CHAP_INITIATOR,
            'chapName': 'test-user',
            'chapSecret': 'test-pass'
        }

        def get_side_effect(*args):
            data = {'value': None}
            if args[1] == CHAP_USER_KEY:
                data['value'] = 'test-user'
            elif args[1] == CHAP_PASS_KEY:
                data['value'] = 'test-pass'
            return data

        mock_client.getVolumeMetaData.side_effect = get_side_effect

        host, auth_username, auth_password = self.driver._create_host(
            self.volume, self.connector)
        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY),
            mock.call.getVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
            mock.call.createHost(
                self.FAKE_HOST,
                optional={'domain': None, 'persona': 2},
                iscsiNames=['iqn.1993-08.org.debian:01:222']),
            mock.call.modifyHost(
                'fakehost',
                expected_mod_request),
            mock.call.getHost(self.FAKE_HOST)
        ]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], self.FAKE_HOST)
        self.assertEqual(auth_username, 'test-user')
        self.assertEqual(auth_password, 'test-pass')

    def test_create_invalid_host(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('Host not found.'),
            {'name': 'fakehost.foo'}]
        mock_client.findHost.return_value = 'fakehost.foo'

        host, auth_username, auth_password = self.driver._create_host(
            self.volume, self.connector)

        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
            mock.call.getHost('fakehost.foo')]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], 'fakehost.foo')
        self.assertEqual(auth_username, None)
        self.assertEqual(auth_password, None)

    def test_create_invalid_host_chap_enabled(self):
        # setup_mock_client drive with CHAP enabled configuration
        # and return the mock HTTP 3PAR client
        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True
        mock_client = self.setup_driver(config=config)

        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            hpexceptions.HTTPNotFound('Host not found.'),
            {'name': 'fakehost.foo'}]
        mock_client.findHost.return_value = 'fakehost.foo'

        def get_side_effect(*args):
            data = {'value': None}
            if args[1] == CHAP_USER_KEY:
                data['value'] = 'test-user'
            elif args[1] == CHAP_PASS_KEY:
                data['value'] = 'test-pass'
            return data

        mock_client.getVolumeMetaData.side_effect = get_side_effect

        expected_mod_request = {
            'chapOperation': mock_client.HOST_EDIT_ADD,
            'chapOperationMode': mock_client.CHAP_INITIATOR,
            'chapName': 'test-user',
            'chapSecret': 'test-pass'
        }

        host, auth_username, auth_password = self.driver._create_host(
            self.volume, self.connector)

        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY),
            mock.call.getVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
            mock.call.modifyHost(
                'fakehost.foo',
                expected_mod_request),
            mock.call.getHost('fakehost.foo')
        ]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], 'fakehost.foo')
        self.assertEqual(auth_username, 'test-user')
        self.assertEqual(auth_password, 'test-pass')

    def test_create_modify_host(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            {'name': self.FAKE_HOST, 'FCPaths': []},
            {'name': self.FAKE_HOST,
             'FCPaths': [{'wwn': '123456789012345'},
                         {'wwn': '123456789054321'}]}]

        host, auth_username, auth_password = self.driver._create_host(
            self.volume, self.connector)

        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.modifyHost(
                self.FAKE_HOST,
                {'pathOperation': 1,
                    'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
            mock.call.getHost(self.FAKE_HOST)]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], self.FAKE_HOST)
        self.assertEqual(auth_username, None)
        self.assertEqual(auth_password, None)
        self.assertEqual(len(host['FCPaths']), 2)

    def test_create_modify_host_chap_enabled(self):
        # setup_mock_client drive with CHAP enabled configuration
        # and return the mock HTTP 3PAR client
        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True
        mock_client = self.setup_driver(config=config)

        mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
        mock_client.getCPG.return_value = {}
        mock_client.getHost.side_effect = [
            {'name': self.FAKE_HOST, 'FCPaths': []},
            {'name': self.FAKE_HOST,
             'FCPaths': [{'wwn': '123456789012345'},
                         {'wwn': '123456789054321'}]}]

        def get_side_effect(*args):
            data = {'value': None}
            if args[1] == CHAP_USER_KEY:
                data['value'] = 'test-user'
            elif args[1] == CHAP_PASS_KEY:
                data['value'] = 'test-pass'
            return data

        mock_client.getVolumeMetaData.side_effect = get_side_effect

        expected_mod_request = {
            'chapOperation': mock_client.HOST_EDIT_ADD,
            'chapOperationMode': mock_client.CHAP_INITIATOR,
            'chapName': 'test-user',
            'chapSecret': 'test-pass'
        }

        host, auth_username, auth_password = self.driver._create_host(
            self.volume, self.connector)

        expected = [
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getCPG(HP3PAR_CPG),
            mock.call.getVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY),
            mock.call.getVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
            mock.call.getHost(self.FAKE_HOST),
            mock.call.modifyHost(
                self.FAKE_HOST,
                {'pathOperation': 1,
                    'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
            mock.call.modifyHost(
                self.FAKE_HOST,
                expected_mod_request
            ),
            mock.call.getHost(self.FAKE_HOST)]

        mock_client.assert_has_calls(expected)

        self.assertEqual(host['name'], self.FAKE_HOST)
        self.assertEqual(auth_username, 'test-user')
        self.assertEqual(auth_password, 'test-pass')
        self.assertEqual(len(host['FCPaths']), 2)

    def test_get_least_used_nsp_for_host_single(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        mock_client.getPorts.return_value = PORTS_RET
        mock_client.getVLUNs.return_value = VLUNS1_RET

        #Setup a single ISCSI IP
        iscsi_ips = ["10.10.220.253"]
        self.driver.configuration.hp3par_iscsi_ips = iscsi_ips

        self.driver.initialize_iscsi_ports()

        nsp = self.driver._get_least_used_nsp_for_host('newhost')
        self.assertEqual(nsp, "1:8:1")

    def test_get_least_used_nsp_for_host_new(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        mock_client.getPorts.return_value = PORTS_RET
        mock_client.getVLUNs.return_value = VLUNS1_RET

        #Setup two ISCSI IPs
        iscsi_ips = ["10.10.220.252", "10.10.220.253"]
        self.driver.configuration.hp3par_iscsi_ips = iscsi_ips

        self.driver.initialize_iscsi_ports()

        # Host 'newhost' does not yet have any iscsi paths,
        # so the 'least used' is returned
        nsp = self.driver._get_least_used_nsp_for_host('newhost')
        self.assertEqual(nsp, "1:8:2")

    def test_get_least_used_nsp_for_host_reuse(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        mock_client.getPorts.return_value = PORTS_RET
        mock_client.getVLUNs.return_value = VLUNS1_RET

        #Setup two ISCSI IPs
        iscsi_ips = ["10.10.220.252", "10.10.220.253"]
        self.driver.configuration.hp3par_iscsi_ips = iscsi_ips

        self.driver.initialize_iscsi_ports()

        # hosts 'foo' and 'bar' already have active iscsi paths
        # the same one should be used
        nsp = self.driver._get_least_used_nsp_for_host('foo')
        self.assertEqual(nsp, "1:8:2")

        nsp = self.driver._get_least_used_nsp_for_host('bar')
        self.assertEqual(nsp, "1:8:1")

    def test_get_least_used_nps_for_host_fc(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        mock_client.getPorts.return_value = PORTS1_RET
        mock_client.getVLUNs.return_value = VLUNS5_RET

        #Setup two ISCSI IPs
        iscsi_ips = ["10.10.220.252", "10.10.220.253"]
        self.driver.configuration.hp3par_iscsi_ips = iscsi_ips

        self.driver.initialize_iscsi_ports()

        nsp = self.driver._get_least_used_nsp_for_host('newhost')
        self.assertNotEqual(nsp, "0:6:3")
        self.assertEqual(nsp, "1:8:1")

    def test_invalid_iscsi_ip(self):
        config = self.setup_configuration()
        config.hp3par_iscsi_ips = ['10.10.220.250', '10.10.220.251']
        config.iscsi_ip_address = '10.10.10.10'
        mock_conf = {
            'getPorts.return_value': {
                'members': [
                    {'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
                     'protocol': 2,
                     'IPAddr': '10.10.220.252',
                     'linkState': 4,
                     'device': [],
                     'iSCSIName': self.TARGET_IQN,
                     'mode': 2,
                     'HWAddr': '2C27D75375D2',
                     'type': 8},
                    {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
                     'protocol': 2,
                     'IPAddr': '10.10.220.253',
                     'linkState': 4,
                     'device': [],
                     'iSCSIName': self.TARGET_IQN,
                     'mode': 2,
                     'HWAddr': '2C27D75375D6',
                     'type': 8}]}}

        # no valid ip addr should be configured.
        self.assertRaises(exception.InvalidInput,
                          self.setup_driver,
                          config=config,
                          mock_conf=mock_conf)

    def test_get_least_used_nsp(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()
        ports = [
            {'portPos': {'node': 1, 'slot': 8, 'cardPort': 2}, 'active': True},
            {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 1, 'slot': 8, 'cardPort': 2}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 2}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}]
        mock_client.getVLUNs.return_value = {'members': ports}

        # in use count
        vluns = self.driver.common.client.getVLUNs()
        nsp = self.driver._get_least_used_nsp(vluns['members'],
                                              ['0:2:1', '1:8:1'])
        self.assertEqual(nsp, '1:8:1')

        ports = [
            {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}]

        mock_client.getVLUNs.return_value = {'members': ports}

        # in use count
        vluns = self.driver.common.client.getVLUNs()
        nsp = self.driver._get_least_used_nsp(vluns['members'],
                                              ['0:2:1', '1:2:1'])
        self.assertEqual(nsp, '1:2:1')

        ports = [
            {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
            {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}]

        mock_client.getVLUNs.return_value = {'members': ports}

        # in use count
        vluns = self.driver.common.client.getVLUNs()
        nsp = self.driver._get_least_used_nsp(vluns['members'],
                                              ['1:1:1', '1:2:1'])
        self.assertEqual(nsp, '1:1:1')

    def test_set_3par_chaps(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        expected = []
        self.driver._set_3par_chaps(
            'test-host', 'test-vol', 'test-host', 'pass')
        mock_client.assert_has_calls(expected)

        # setup_mock_client drive with CHAP enabled configuration
        # and return the mock HTTP 3PAR client
        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True
        mock_client = self.setup_driver(config=config)

        expected_mod_request = {
            'chapOperation': mock_client.HOST_EDIT_ADD,
            'chapOperationMode': mock_client.CHAP_INITIATOR,
            'chapName': 'test-host',
            'chapSecret': 'fake'
        }

        expected = [
            mock.call.modifyHost('test-host', expected_mod_request)
        ]
        self.driver._set_3par_chaps(
            'test-host', 'test-vol', 'test-host', 'fake')
        mock_client.assert_has_calls(expected)

    @mock.patch('cinder.volume.utils.generate_password')
    def test_do_export(self, mock_utils):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        volume = {'host': 'test-host@3pariscsi',
                  'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
        mock_utils.return_value = 'random-pass'
        mock_client.getHostVLUNs.return_value = [
            {'active': True,
             'volumeName': self.VOLUME_3PAR_NAME,
             'lun': None, 'type': 0,
             'remoteName': 'iqn.1993-08.org.debian:01:222'}
        ]
        mock_client.getHost.return_value = {
            'name': 'osv-0DM4qZEVSKON-DXN-NwVpw',
            'initiatorChapEnabled': True
        }
        mock_client.getVolumeMetaData.return_value = {
            'value': 'random-pass'
        }

        expected = []
        expected_model = {'provider_auth': None}
        model = self.driver._do_export(volume)

        mock_client.assert_has_calls(expected)
        self.assertEqual(expected_model, model)

        mock_client.reset_mock()

        # setup_mock_client drive with CHAP enabled configuration
        # and return the mock HTTP 3PAR client
        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True
        mock_client = self.setup_driver(config=config)

        volume = {'host': 'test-host@3pariscsi',
                  'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
        mock_utils.return_value = 'random-pass'
        mock_client.getHostVLUNs.return_value = [
            {'active': True,
             'volumeName': self.VOLUME_3PAR_NAME,
             'lun': None, 'type': 0,
             'remoteName': 'iqn.1993-08.org.debian:01:222'}
        ]
        mock_client.getHost.return_value = {
            'name': 'osv-0DM4qZEVSKON-DXN-NwVpw',
            'initiatorChapEnabled': True
        }
        mock_client.getVolumeMetaData.return_value = {
            'value': 'random-pass'
        }

        expected = [
            mock.call.getHostVLUNs('test-host'),
            mock.call.getHost('test-host'),
            mock.call.getVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
            mock.call.setVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
            mock.call.setVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')
        ]
        expected_model = {'provider_auth': 'CHAP test-host random-pass'}

        model = self.driver._do_export(volume)
        mock_client.assert_has_calls(expected)
        self.assertEqual(expected_model, model)

    @mock.patch('cinder.volume.utils.generate_password')
    def test_do_export_host_not_found(self, mock_utils):
        # setup_mock_client drive with CHAP enabled configuration
        # and return the mock HTTP 3PAR client
        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True
        mock_client = self.setup_driver(config=config)

        volume = {'host': 'test-host@3pariscsi',
                  'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
        mock_utils.return_value = "random-pass"
        mock_client.getHostVLUNs.side_effect = hpexceptions.HTTPNotFound(
            'fake')

        mock_client.getVolumeMetaData.return_value = {
            'value': 'random-pass'
        }

        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True

        expected = [
            mock.call.getHostVLUNs('test-host'),
            mock.call.setVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
            mock.call.setVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')
        ]
        expected_model = {'provider_auth': 'CHAP test-host random-pass'}

        model = self.driver._do_export(volume)
        mock_client.assert_has_calls(expected)
        self.assertEqual(expected_model, model)

    @mock.patch('cinder.volume.utils.generate_password')
    def test_do_export_host_chap_disabled(self, mock_utils):
        # setup_mock_client drive with CHAP enabled configuration
        # and return the mock HTTP 3PAR client
        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True
        mock_client = self.setup_driver(config=config)

        volume = {'host': 'test-host@3pariscsi',
                  'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
        mock_utils.return_value = 'random-pass'
        mock_client.getHostVLUNs.return_value = [
            {'active': True,
             'volumeName': self.VOLUME_3PAR_NAME,
             'lun': None, 'type': 0,
             'remoteName': 'iqn.1993-08.org.debian:01:222'}
        ]
        mock_client.getHost.return_value = {
            'name': 'fake-host',
            'initiatorChapEnabled': False
        }
        mock_client.getVolumeMetaData.return_value = {
            'value': 'random-pass'
        }

        mock_client.reset_mock()

        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True

        expected = [
            mock.call.getHostVLUNs('test-host'),
            mock.call.getHost('test-host'),
            mock.call.getVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
            mock.call.setVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
            mock.call.setVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')
        ]
        expected_model = {'provider_auth': 'CHAP test-host random-pass'}

        model = self.driver._do_export(volume)
        mock_client.assert_has_calls(expected)
        self.assertEqual(expected_model, model)

    @mock.patch('cinder.volume.utils.generate_password')
    def test_do_export_no_active_vluns(self, mock_utils):
        # setup_mock_client drive with CHAP enabled configuration
        # and return the mock HTTP 3PAR client
        config = self.setup_configuration()
        config.hp3par_iscsi_chap_enabled = True
        mock_client = self.setup_driver(config=config)

        volume = {'host': 'test-host@3pariscsi',
                  'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
        mock_utils.return_value = "random-pass"
        mock_client.getHostVLUNs.return_value = [
            {'active': False,
             'volumeName': self.VOLUME_3PAR_NAME,
             'lun': None, 'type': 0,
             'remoteName': 'iqn.1993-08.org.debian:01:222'}
        ]
        mock_client.getHost.return_value = {
            'name': 'fake-host',
            'initiatorChapEnabled': True
        }
        mock_client.getVolumeMetaData.return_value = {
            'value': 'random-pass'
        }

        expected = [
            mock.call.getHostVLUNs('test-host'),
            mock.call.getHost('test-host'),
            mock.call.setVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
            mock.call.setVolumeMetaData(
                'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')
        ]
        expected_model = {'provider_auth': 'CHAP test-host random-pass'}

        model = self.driver._do_export(volume)
        mock_client.assert_has_calls(expected)
        self.assertEqual(model, expected_model)

    def test_ensure_export(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        volume = {'host': 'test-host@3pariscsi',
                  'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}

        mock_client.getAllVolumeMetaData.return_value = {
            'total': 0,
            'members': []
        }

        model = self.driver.ensure_export(None, volume)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getAllVolumeMetaData('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.logout()
        ]

        expected_model = {'provider_auth': None}

        mock_client.assert_has_calls(expected)
        self.assertEqual(model, expected_model)

        mock_client.getAllVolumeMetaData.return_value = {
            'total': 2,
            'members': [
                {
                    'creationTimeSec': 1406074222,
                    'value': 'fake-host',
                    'key': CHAP_USER_KEY,
                    'creationTime8601': '2014-07-22T17:10:22-07:00'
                },
                {
                    'creationTimeSec': 1406074222,
                    'value': 'random-pass',
                    'key': CHAP_PASS_KEY,
                    'creationTime8601': '2014-07-22T17:10:22-07:00'
                }
            ]
        }

        model = self.driver.ensure_export(None, volume)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.getAllVolumeMetaData('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.logout()
        ]

        expected_model = {'provider_auth': "CHAP fake-host random-pass"}

        mock_client.assert_has_calls(expected)
        self.assertEqual(model, expected_model)

    def test_ensure_export_missing_volume(self):
        # setup_mock_client drive with default configuration
        # and return the mock HTTP 3PAR client
        mock_client = self.setup_driver()

        volume = {'host': 'test-host@3pariscsi',
                  'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}

        mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound(
            'fake')

        model = self.driver.ensure_export(None, volume)

        expected = [
            mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
            mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
            mock.call.logout()
        ]

        expected_model = None

        mock_client.assert_has_calls(expected)
        self.assertEqual(model, expected_model)


VLUNS5_RET = ({'members':
               [{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
                 'active': True},
                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
                 'active': True}]})

PORTS_RET = ({'members':
              [{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
                'protocol': 2,
                'IPAddr': '10.10.220.252',
                'linkState': 4,
                'device': [],
                'iSCSIName': 'iqn.2000-05.com.3pardata:21820002ac00383d',
                'mode': 2,
                'HWAddr': '2C27D75375D2',
                'type': 8},
               {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
                'protocol': 2,
                'IPAddr': '10.10.220.253',
                'linkState': 4,
                'device': [],
                'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
                'mode': 2,
                'HWAddr': '2C27D75375D6',
                'type': 8}]})

VLUNS1_RET = ({'members':
               [{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
                 'hostname': 'foo', 'active': True},
                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
                 'hostname': 'bar', 'active': True},
                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
                 'hostname': 'bar', 'active': True},
                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
                 'hostname': 'bar', 'active': True}]})

PORTS1_RET = ({'members':
               [{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
                 'protocol': 2,
                 'IPAddr': '10.10.120.252',
                 'linkState': 4,
                 'device': [],
                 'iSCSIName': 'iqn.2000-05.com.3pardata:21820002ac00383d',
                 'mode': 2,
                 'HWAddr': '2C27D75375D2',
                 'type': 8},
                {'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
                 'protocol': 2,
                 'IPAddr': '10.10.220.253',
                 'linkState': 4,
                 'device': [],
                 'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
                 'mode': 2,
                 'HWAddr': '2C27D75375D6',
                 'type': 8},
                {'portWWN': '20210002AC00383D',
                 'protocol': 1,
                 'linkState': 4,
                 'mode': 2,
                 'device': ['cage2'],
                 'nodeWWN': '20210002AC00383D',
                 'type': 2,
                 'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]})
