# Copyright (C) 2020, 2024, Hitachi, Ltd.
# Copyright (C) 2025, Hitachi Vantara
#
#    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 Hitachi HBSD Driver."""

import json
from unittest import mock

from oslo_config import cfg
import requests

from cinder import context as cinder_context
from cinder.db.sqlalchemy import api as sqlalchemy_api
from cinder import exception
from cinder import objects
from cinder.objects import fields
from cinder.objects import group_snapshot as obj_group_snap
from cinder.objects import snapshot as obj_snap
from cinder.tests.unit import fake_group
from cinder.tests.unit import fake_group_snapshot
from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import fake_volume
from cinder.tests.unit import test
from cinder.volume import configuration as conf
from cinder.volume import driver
from cinder.volume.drivers.hitachi import hbsd_common
from cinder.volume.drivers.hitachi import hbsd_fc
from cinder.volume.drivers.hitachi import hbsd_replication
from cinder.volume.drivers.hitachi import hbsd_rest
from cinder.volume.drivers.hitachi import hbsd_rest_api
from cinder.volume.drivers.hitachi import hbsd_rest_fc
from cinder.volume.drivers.hitachi import hbsd_utils
from cinder.volume import volume_types
from cinder.volume import volume_utils
from cinder.zonemanager import utils as fczm_utils

# Configuration parameter values
CONFIG_MAP = {
    'serial': '886000123456',
    'my_ip': '127.0.0.1',
    'rest_server_ip_addr': '172.16.18.108',
    'rest_server_ip_port': '23451',
    'port_id': 'CL1-A',
    'host_grp_name': 'HBSD-0123456789abcdef',
    'host_mode': 'LINUX/IRIX',
    'host_wwn': '0123456789abcdef',
    'target_wwn': '1111111123456789',
    'user_id': 'user',
    'user_pass': 'password',
    'pool_name': 'test_pool',
    'auth_user': 'auth_user',
    'auth_password': 'auth_password',
}

REMOTE_CONFIG_MAP = {
    'serial': '886000456789',
    'my_ip': '127.0.0.1',
    'rest_server_ip_addr': '172.16.18.107',
    'rest_server_ip_port': '334',
    'port_id': 'CL2-B',
    'host_grp_name': 'HBSD-0123456789abcdef',
    'host_mode': 'LINUX/IRIX',
    'host_wwn': '0123456789abcdef',
    'target_wwn': '2222222234567891',
    'user_id': 'remote-user',
    'user_pass': 'remote-password',
    'pool_name': 'remote_pool',
    'auth_user': 'remote_user',
    'auth_password': 'remote_password',
}

# Dummy response for FC zoning device mapping
DEVICE_MAP = {
    'fabric_name': {
        'initiator_port_wwn_list': [CONFIG_MAP['host_wwn']],
        'target_port_wwn_list': [CONFIG_MAP['target_wwn']]}}

REMOTE_DEVICE_MAP = {
    'fabric_name': {
        'initiator_port_wwn_list': [REMOTE_CONFIG_MAP['host_wwn']],
        'target_port_wwn_list': [REMOTE_CONFIG_MAP['target_wwn']]}}

DEFAULT_CONNECTOR = {
    'host': 'host',
    'ip': CONFIG_MAP['my_ip'],
    'wwpns': [CONFIG_MAP['host_wwn']],
    'multipath': False,
}

REMOTE_DEFAULT_CONNECTOR = {
    'host': 'host',
    'ip': REMOTE_CONFIG_MAP['my_ip'],
    'wwpns': [REMOTE_CONFIG_MAP['host_wwn']],
    'multipath': False,
}

CTXT = cinder_context.get_admin_context()

TEST_VOLUME = []
for i in range(9):
    volume = {}
    volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
    volume['name'] = 'test-volume{0:d}'.format(i)
    volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
    if i == 3 or i == 8:
        volume['provider_location'] = None
    elif i == 4:
        volume['provider_location'] = json.dumps(
            {'pldev': 4, 'sldev': 4,
             'remote-copy': hbsd_utils.REP_TYPE_ASYNC})
    elif i == 5:
        volume['provider_location'] = json.dumps(
            {'pldev': 5, 'sldev': 5,
             'remote-copy': hbsd_utils.REP_TYPE_ASYNC})
        volume['replication_status'] = fields.ReplicationStatus.ENABLED
    elif i == 6:
        volume['provider_location'] = json.dumps(
            {'pldev': 6, 'sldev': 6,
             'remote-copy': hbsd_utils.REP_TYPE_ASYNC})
        volume['replication_status'] = fields.ReplicationStatus.FAILED_OVER
    elif i == 7:
        volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
        volume['provider_location'] = json.dumps(
            {'pldev': 7,
             'remote-copy': hbsd_utils.REP_TYPE_ASYNC})
        volume['replication_status'] = fields.ReplicationStatus.ENABLED
    else:
        volume['provider_location'] = '{0:d}'.format(i)
    volume['size'] = 128
    if i == 2:
        volume['status'] = 'in-use'
    elif i == 8:
        volume['status'] = None
    else:
        volume['status'] = 'available'
    volume = fake_volume.fake_volume_obj(CTXT, **volume)
    volume.volume_type = fake_volume.fake_volume_type_obj(CTXT)
    TEST_VOLUME.append(volume)


def _volume_get(context, volume_id):
    """Return predefined volume info."""
    return TEST_VOLUME[int(volume_id.replace("-", ""))]


TEST_SNAPSHOT = []
for i in range(6):
    snapshot = {}
    snapshot['id'] = '10000000-0000-0000-0000-{0:012d}'.format(i)
    snapshot['name'] = 'TEST_SNAPSHOT{0:d}'.format(i)
    snapshot['provider_location'] = '{0:d}'.format(i + 1)
    snapshot['status'] = 'available'
    snapshot['volume_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
    snapshot['volume'] = _volume_get(None, snapshot['volume_id'])
    snapshot['volume_name'] = 'test-volume{0:d}'.format(i)
    snapshot['volume_size'] = 128
    if i == 5:
        snapshot['provider_location'] = json.dumps(
            {'sldev': 5})
    snapshot = obj_snap.Snapshot._from_db_object(
        CTXT, obj_snap.Snapshot(),
        fake_snapshot.fake_db_snapshot(**snapshot))
    TEST_SNAPSHOT.append(snapshot)

TEST_GROUP = []
for i in range(2):
    group = {}
    group['id'] = '20000000-0000-0000-0000-{0:012d}'.format(i)
    group['status'] = 'available'
    group = fake_group.fake_group_obj(CTXT, **group)
    TEST_GROUP.append(group)

TEST_GROUP_SNAP = []
group_snapshot = {}
group_snapshot['id'] = '30000000-0000-0000-0000-{0:012d}'.format(0)
group_snapshot['status'] = 'available'
group_snapshot = obj_group_snap.GroupSnapshot._from_db_object(
    CTXT, obj_group_snap.GroupSnapshot(),
    fake_group_snapshot.fake_db_group_snapshot(**group_snapshot))
TEST_GROUP_SNAP.append(group_snapshot)

# Dummy response for REST API
POST_SESSIONS_RESULT = {
    "token": "b74777a3-f9f0-4ea8-bd8f-09847fac48d3",
    "sessionId": 0,
}

REMOTE_POST_SESSIONS_RESULT = {
    "token": "b74777a3-f9f0-4ea8-bd8f-09847fac48d4",
    "sessionId": 0,
}

GET_PORTS_RESULT = {
    "data": [
        {
            "portId": CONFIG_MAP['port_id'],
            "portType": "FIBRE",
            "portAttributes": [
                "TAR",
                "MCU",
                "RCU",
                "ELUN"
            ],
            "fabricMode": True,
            "portConnection": "PtoP",
            "lunSecuritySetting": True,
            "wwn": CONFIG_MAP['target_wwn'],
        },
    ],
}

REMOTE_GET_PORTS_RESULT = {
    "data": [
        {
            "portId": REMOTE_CONFIG_MAP['port_id'],
            "portType": "FIBRE",
            "portAttributes": [
                "TAR",
                "MCU",
                "RCU",
                "ELUN"
            ],
            "fabricMode": True,
            "portConnection": "PtoP",
            "lunSecuritySetting": True,
            "wwn": REMOTE_CONFIG_MAP['target_wwn'],
        },
    ],
}

GET_HOST_WWNS_RESULT = {
    "data": [
        {
            "hostGroupNumber": 0,
            "hostWwn": CONFIG_MAP['host_wwn'],
        },
    ],
}

REMOTE_GET_HOST_WWNS_RESULT = {
    "data": [
        {
            "hostGroupNumber": 0,
            "hostWwn": REMOTE_CONFIG_MAP['host_wwn'],
        },
    ],
}

COMPLETED_SUCCEEDED_RESULT = {
    "status": "Completed",
    "state": "Succeeded",
    "affectedResources": ('a/b/c/1',),
}

REMOTE_COMPLETED_SUCCEEDED_RESULT = {
    "status": "Completed",
    "state": "Succeeded",
    "affectedResources": ('a/b/c/2',),
}

COMPLETED_FAILED_RESULT_LU_DEFINED = {
    "status": "Completed",
    "state": "Failed",
    "error": {
        "errorCode": {
            "SSB1": "B958",
            "SSB2": "015A",
        },
    },
}

GET_LDEV_RESULT = {
    "emulationType": "OPEN-V-CVS",
    "blockCapacity": 2097152,
    "attributes": ["CVS", "HDP"],
    "status": "NML",
    "poolId": 30,
    "dataReductionStatus": "DISABLED",
    "dataReductionMode": "disabled",
    "label": "00000000000000000000000000000000",
}

GET_LDEV_RESULT_SPLIT = {
    "emulationType": "OPEN-V-CVS",
    "blockCapacity": 2097152,
    "attributes": ["CVS", "HDP"],
    "status": "NML",
    "poolId": 30,
    "dataReductionStatus": "DISABLED",
    "dataReductionMode": "disabled",
    "label": "00000000000000000000000000000004",
}

GET_LDEV_RESULT_LABEL = {
    "emulationType": "OPEN-V-CVS",
    "blockCapacity": 2097152,
    "attributes": ["CVS", "HDP"],
    "status": "NML",
    "poolId": 30,
    "dataReductionStatus": "DISABLED",
    "dataReductionMode": "disabled",
    "label": "00000000000000000000000000000001",
}

GET_LDEV_RESULT_MAPPED = {
    "emulationType": "OPEN-V-CVS",
    "blockCapacity": 2097152,
    "attributes": ["CVS", "HDP"],
    "status": "NML",
    "ports": [
        {
            "portId": CONFIG_MAP['port_id'],
            "hostGroupNumber": 0,
            "hostGroupName": CONFIG_MAP['host_grp_name'],
            "lun": 1
        },
    ],
}

GET_LDEV_RESULT_PAIR = {
    "emulationType": "OPEN-V-CVS",
    "blockCapacity": 2097152,
    "attributes": ["CVS", "HDP", "HTI"],
    "status": "NML",
    "label": "10000000000000000000000000000000",
}

GET_LDEV_RESULT_REP = {
    "emulationType": "OPEN-V-CVS",
    "blockCapacity": 2097152,
    "attributes": ["CVS", "HDP", "HORC"],
    "status": "NML",
    "numOfPorts": 1,
    "label": "00000000000000000000000000000004",
}

GET_LDEV_RESULT_REP_LABEL = {
    "emulationType": "OPEN-V-CVS",
    "blockCapacity": 2097152,
    "attributes": ["CVS", "HDP", "HORC"],
    "status": "NML",
    "numOfPorts": 1,
    "label": "00000000000000000000000000000001",
}

GET_LDEV_RESULT_REP_PAIR = {
    "emulationType": "OPEN-V-CVS",
    "blockCapacity": 2097152,
    "attributes": ["CVS", "HDP", "HORC", "HTI"],
    "status": "NML",
    "numOfPorts": 1,
}

GET_POOLS_RESULT = {
    "data": [
        {
            "poolId": 30,
            "poolName": CONFIG_MAP['pool_name'],
            "availableVolumeCapacity": 480144,
            "totalPoolCapacity": 507780,
            "totalLocatedCapacity": 71453172,
            "virtualVolumeCapacityRate": -1,
        },
    ],
}

GET_SNAPSHOTS_RESULT = {
    "data": [
        {
            "primaryOrSecondary": "S-VOL",
            "status": "PSUS",
            "pvolLdevId": 0,
            "muNumber": 1,
            "svolLdevId": 1,
        },
    ],
}

GET_SNAPSHOTS_RESULT_SMPL = {
    "data": [
        {
            "primaryOrSecondary": "S-VOL",
            "status": "SMPL",
            "pvolLdevId": 0,
            "muNumber": 1,
            "svolLdevId": 1,
        },
    ],
}

GET_SNAPSHOTS_RESULT_PAIR = {
    "data": [
        {
            "primaryOrSecondary": "S-VOL",
            "status": "PAIR",
            "pvolLdevId": 0,
            "muNumber": 1,
            "svolLdevId": 1,
        },
    ],
}

GET_SNAPSHOTS_RESULT_BUSY = {
    "data": [
        {
            "primaryOrSecondary": "P-VOL",
            "status": "PSUP",
            "pvolLdevId": 0,
            "muNumber": 1,
            "svolLdevId": 1,
        },
    ],
}

GET_SNAPSHOTS_RESULT_PSUS = {
    "data": [
        {
            "primaryOrSecondary": "S-VOL",
            "status": "PSUS",
            "pvolLdevId": 4,
            "muNumber": 1,
            "svolLdevId": 5,
        },
    ],
}

GET_LUNS_RESULT = {
    "data": [
        {
            "ldevId": 0,
            "lun": 1,
        },
    ],
}

GET_HOST_GROUP_RESULT = {
    "hostGroupName": CONFIG_MAP['host_grp_name'],
}

GET_HOST_GROUPS_RESULT = {
    "data": [
        {
            "hostGroupNumber": 0,
            "portId": CONFIG_MAP['port_id'],
            "hostGroupName": "HBSD-test",
        },
    ],
}

GET_HOST_GROUPS_RESULT_PAIR = {
    "data": [
        {
            "hostGroupNumber": 1,
            "portId": CONFIG_MAP['port_id'],
            "hostGroupName": "HBSD-pair00",
        },
    ],
}

REMOTE_GET_HOST_GROUPS_RESULT_PAIR = {
    "data": [
        {
            "hostGroupNumber": 1,
            "portId": REMOTE_CONFIG_MAP['port_id'],
            "hostGroupName": "HBSD-pair00",
        },
    ],
}

GET_LDEVS_RESULT = {
    "data": [
        {
            "ldevId": 0,
            "label": "15960cc738c94c5bb4f1365be5eeed44",
        },
        {
            "ldevId": 1,
            "label": "15960cc738c94c5bb4f1365be5eeed45",
        },
    ],
}

GET_REMOTE_MIRROR_COPYPAIR_RESULT = {
    'pvolLdevId': 4,
    'svolLdevId': 4,
    'pvolStatus': 'PAIR',
    'svolStatus': 'PAIR',
    'replicationType': hbsd_utils.REP_TYPE_ASYNC,
    'pvolJournalId': 0,
    'svolJournalId': 0,
}

GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS = {
    'pvolLdevId': 5,
    'svolLdevId': 5,
    'pvolStatus': 'PSUS',
    'svolStatus': 'SSWS',
    'replicationType': hbsd_utils.REP_TYPE_ASYNC,
    'pvolJournalId': 0,
    'svolJournalId': 0,
}

GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS_FAILBACK = {
    'pvolLdevId': 6,
    'svolLdevId': 6,
    'pvolStatus': 'PSUS',
    'svolStatus': 'SSWS',
    'replicationType': hbsd_utils.REP_TYPE_ASYNC,
    'pvolJournalId': 0,
    'svolJournalId': 0,
}

GET_REMOTE_MIRROR_COPYPAIR_RESULT_SPLIT = {
    'pvolLdevId': 6,
    'svolLdevId': 6,
    'pvolStatus': 'PSUE',
    'svolStatus': 'SSWS',
    'replicationType': hbsd_utils.REP_TYPE_ASYNC,
    'pvolJournalId': 0,
    'svolJournalId': 0,
}

GET_REMOTE_MIRROR_COPYPAIR_RESULT_PSUS = {
    'pvolLdevId': 6,
    'svolLdevId': 6,
    'pvolStatus': 'PSUS',
    'svolStatus': 'SSUS',
    'replicationType': hbsd_utils.REP_TYPE_ASYNC,
    'pvolJournalId': 0,
    'svolJournalId': 0,
}

GET_REMOTE_MIRROR_COPYGROUP_RESULT = {
    'copyGroupName': 'HBSD-127.0.0.100U00',
    'copyPairs': [GET_REMOTE_MIRROR_COPYPAIR_RESULT],
}

GET_REMOTE_MIRROR_COPYGROUP_RESULT_ERROR = {
    "errorSource": "<URL>",
    "message": "<message>",
    "solution": "<solution>",
    "messageId": hbsd_replication._MSGID_INSTANCE_CANNOT_OPERATED,
    "errorCode": {
                   "SSB1": "",
                   "SSB2": "",
    }
}

GET_REMOTE_MIRROR_COPYGROUP_RESULT_SSWS = {
    'copyGroupName': 'HBSD-127.0.0.100U00',
    'copyPairs': [GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS_FAILBACK],
}

GET_REMOTE_MIRROR_COPYGROUPS_RESULT_PAIR = {
    "data": [
        {
            'copyGroupName': 'HBSD-127.0.0.100U00',
            'copyPairs': [GET_REMOTE_MIRROR_COPYPAIR_RESULT],
        },
    ],
}

GET_REMOTE_MIRROR_COPYGROUPS_RESULT_SSWS = {
    "data": [
        {
            'copyGroupName': 'HBSD-127.0.0.100U00',
            'copyPairs': [GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS_FAILBACK],
        },
    ],
}

GET_JOURNAL_RESULT = {
    'firstLdevId': 1,
}

REMOTE_GET_JOURNAL_RESULT = {
    'firstLdevId': 2,
}


GET_JOURNAL_RESULT_MESSAGEID_KART40054E = {
    'firstLdevId': 2,
    'pvolJournalId': 10,
    'svolJournalId': 10,
    'journalId': 10,
    "data": [
        {
            "test1": "test1",
            "journalId": 0,
            "test2": "test2",
        }
    ],
    "errorSource": "testurl",
    "message": "testmessage",
    "solution": "test",
    "messageId": "KART40054-E",
    "errorCode": {
                   "SSB1": "1",
                   "SSB2": "2",
    }
}

GET_JOURNAL_RESULT_MESSAGEID_KART40046E = {
    'firstLdevId': 2,
    'pvolJournalId': 10,
    'svolJournalId': 10,
    'journalId': 10,
    "data": [
        {
            "test1": "test1",
            "journalId": 0,
            "test2": "test2",
        }
    ],
    "errorSource": "testurl",
    "message": "testmessage",
    "solution": "test",
    "messageId": "KART40046-E",
    "errorCode": {
                   "SSB1": "1",
                   "SSB2": "2",
    }
}

NOTFOUND_RESULT = {
    "data": [],
}

ERROR_RESULT = {
    "errorSource": "<URL>",
    "message": "<message>",
    "solution": "<solution>",
    "messageId": "<messageId>",
    "errorCode": {
                   "SSB1": "",
                   "SSB2": "",
    }
}

global_counter = 0


def _brick_get_connector_properties(multipath=False, enforce_multipath=False):
    """Return a predefined connector object."""
    return DEFAULT_CONNECTOR


class FakeLookupService():
    """Dummy FC zoning mapping lookup service class."""

    def get_device_mapping_from_network(self, initiator_wwns, target_wwns):
        """Return predefined FC zoning mapping."""
        return DEVICE_MAP


class FakeResponse():

    def __init__(self, status_code, data=None, headers=None):
        self.status_code = status_code
        self.data = data
        self.text = data
        self.content = data
        self.headers = {'Content-Type': 'json'} if headers is None else headers

    def json(self):
        return self.data


class HBSDREPLICATIONFCDriverTest(test.TestCase):
    """Unit test class for HBSD REPLICATION interface fibre channel module."""

    test_existing_ref = {'source-id': '1'}
    test_existing_ref_name = {
        'source-name': '15960cc7-38c9-4c5b-b4f1-365be5eeed45'}

    def setUp(self):
        """Set up the test environment."""
        def _set_required(opts, required):
            for opt in opts:
                opt.required = required

        # Initialize Cinder and avoid checking driver options.
        rest_required_opts = [
            opt for opt in hbsd_rest.REST_VOLUME_OPTS if opt.required]
        common_required_opts = [
            opt for opt in hbsd_common.COMMON_VOLUME_OPTS if opt.required]
        _set_required(rest_required_opts, False)
        _set_required(common_required_opts, False)
        super(HBSDREPLICATIONFCDriverTest, self).setUp()
        _set_required(rest_required_opts, True)
        _set_required(common_required_opts, True)

        self.configuration = conf.Configuration(
            hbsd_rest.REST_VOLUME_OPTS + hbsd_rest.REST_PAIR_OPTS +
            hbsd_common.COMMON_VOLUME_OPTS + hbsd_common.COMMON_PORT_OPTS +
            hbsd_common.COMMON_PAIR_OPTS + hbsd_common.COMMON_NAME_OPTS +
            hbsd_common.COMMON_EXTEND_OPTS + hbsd_replication._REP_OPTS +
            hbsd_replication.COMMON_REPLICATION_OPTS +
            hbsd_replication.COMMON_MIRROR_OPTS +
            hbsd_replication.ISCSI_MIRROR_OPTS +
            hbsd_replication.REST_MIRROR_OPTS +
            hbsd_replication.REST_MIRROR_API_OPTS +
            hbsd_replication.REST_MIRROR_SSL_OPTS +
            hbsd_rest_fc.FC_VOLUME_OPTS,
            conf.SHARED_CONF_GROUP)

        self.ctxt = cinder_context.get_admin_context()
        self._setup_config()
        self._setup_driver()

    def _setup_config(self):
        """Set configuration parameter values."""
        self.override_config('volume_backend_name', "RESTFC",
                             group=conf.SHARED_CONF_GROUP)
        self.override_config(
            'volume_driver',
            "cinder.volume.drivers.hitachi.hbsd_fc.HBSDFCDriver",
            group=conf.SHARED_CONF_GROUP)
        self.override_config('reserved_percentage', "0",
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('use_multipath_for_image_xfer', False,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('enforce_multipath_for_image_xfer', False,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('max_over_subscription_ratio', 500.0,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('driver_ssl_cert_verify', False,
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('hitachi_storage_id', CONFIG_MAP['serial'],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_pools', ["30"],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_snap_pool', None,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_ldev_range', "0-1",
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_target_ports', [CONFIG_MAP['port_id']],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_compute_target_ports',
                             [CONFIG_MAP['port_id']],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_group_create', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_group_delete', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_copy_speed', 3,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_copy_check_interval', 3,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_async_copy_check_interval', 10,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_port_scheduler', False,
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('san_login', CONFIG_MAP['user_id'],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('san_password', CONFIG_MAP['user_pass'],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('san_ip', CONFIG_MAP['rest_server_ip_addr'],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('san_api_port', CONFIG_MAP['rest_server_ip_port'],
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('hitachi_rest_disable_io_wait', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_tcp_keepalive', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_discard_zero_page', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_lun_timeout', hbsd_rest._LUN_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_lun_retry_interval',
                             hbsd_rest._LUN_RETRY_INTERVAL,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_restore_timeout',
                             hbsd_rest._RESTORE_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_state_transition_timeout',
                             hbsd_rest._STATE_TRANSITION_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_lock_timeout',
                             hbsd_rest_api._LOCK_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_timeout',
                             hbsd_rest_api._REST_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_extend_timeout',
                             hbsd_rest_api._EXTEND_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_exec_retry_interval',
                             hbsd_rest_api._EXEC_RETRY_INTERVAL,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_connect_timeout',
                             hbsd_rest_api._DEFAULT_CONNECT_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_job_api_response_timeout',
                             hbsd_rest_api._JOB_API_RESPONSE_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_get_api_response_timeout',
                             hbsd_rest_api._GET_API_RESPONSE_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_server_busy_timeout',
                             hbsd_rest_api._REST_SERVER_BUSY_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_keep_session_loop_interval',
                             hbsd_rest_api._KEEP_SESSION_LOOP_INTERVAL,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_another_ldev_mapped_retry_timeout',
                             hbsd_rest_api._ANOTHER_LDEV_MAPPED_RETRY_TIMEOUT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_tcp_keepidle',
                             hbsd_rest_api._TCP_KEEPIDLE,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_tcp_keepintvl',
                             hbsd_rest_api._TCP_KEEPINTVL,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_tcp_keepcnt',
                             hbsd_rest_api._TCP_KEEPCNT,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_host_mode_options', [],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_use_object_caching', False,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_max_request_workers',
                             hbsd_rest_api._MAX_REQUEST_WORKERS,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_zoning_request', False,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_extend_snapshot_volumes', False,
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('use_chap_auth', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('chap_username', CONFIG_MAP['auth_user'],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('chap_password', CONFIG_MAP['auth_password'],
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('san_thin_provision', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('san_private_key', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('san_clustername', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('san_ssh_port', '22',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('san_is_local', False,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('ssh_conn_timeout', '30',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('ssh_min_pool_conn', '1',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('ssh_max_pool_conn', '5',
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('hitachi_replication_status_check_short_interval',
                             5, group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_replication_status_check_long_interval',
                             10 * 60,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_replication_status_check_timeout',
                             24 * 60 * 60,
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('hitachi_replication_number', 0,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_pair_target_number', 0,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_rest_pair_target_ports',
                             [CONFIG_MAP['port_id']],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_quorum_disk_id', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_replication_copy_speed', 3,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_storage_id', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_pool', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_snap_pool', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_ldev_range', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_target_ports', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_compute_target_ports', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_pair_target_number', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_rest_user', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_rest_password', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_rest_api_ip', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_rest_api_port', '',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_set_mirror_reserve_attribute', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_path_group_id', 0,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_rest_pair_target_ports', [],
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_auth_password', None,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_auth_user', None,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_use_chap_auth', False,
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('hitachi_mirror_ssl_cert_verify', False,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_mirror_ssl_cert_path', '',
                             group=conf.SHARED_CONF_GROUP)

        self.override_config('hitachi_replication_mun', 1,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_replication_journal_size', '10',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_replication_journal_overflow_tolerance',
                             60, group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_replication_journal_use_cache', True,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_replication_journal_transfer_speed', 256,
                             group=conf.SHARED_CONF_GROUP)
        self.override_config('hitachi_replication_journal_creation_speed', 'L',
                             group=conf.SHARED_CONF_GROUP)
        self.override_config(
            'hitachi_replication_journal_path_failure_tolerance', 5,
            group=conf.SHARED_CONF_GROUP)

        replication_device = [
            {'backend_id': 'backend2',
             'storage_id': REMOTE_CONFIG_MAP['serial'],
             'pool': '40',
             'snap_pool': None,
             'ldev_range': '2-3',
             'target_ports': REMOTE_CONFIG_MAP['port_id'],
             'compute_target_ports': REMOTE_CONFIG_MAP['port_id'],
             'pair_target_number': '0',
             'san_login': REMOTE_CONFIG_MAP['user_id'],
             'san_password': REMOTE_CONFIG_MAP['user_pass'],
             'rest_pair_target_ports': REMOTE_CONFIG_MAP['port_id'],
             'san_ip': REMOTE_CONFIG_MAP['rest_server_ip_addr'],
             'san_api_port': REMOTE_CONFIG_MAP['rest_server_ip_port']}
        ]
        self.override_config('replication_device', replication_device,
                             group=conf.SHARED_CONF_GROUP)

        CONF = cfg.CONF
        CONF.my_ip = CONFIG_MAP['my_ip']

    def _fake_safe_get(self, value):
        """Retrieve a configuration value avoiding throwing an exception."""
        try:
            val = getattr(self.configuration, value)
        except AttributeError:
            val = None
        return val

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(
        volume_utils, 'brick_get_connector_properties',
        side_effect=_brick_get_connector_properties)
    def _setup_driver(
            self, brick_get_connector_properties=None, request=None):
        """Set up the driver environment."""
        self.driver = hbsd_fc.HBSDFCDriver(
            configuration=self.configuration)

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method == 'POST':
                    return FakeResponse(200, POST_SESSIONS_RESULT)
                elif '/ports' in url:
                    return FakeResponse(200, GET_PORTS_RESULT)
                elif '/host-wwns' in url:
                    return FakeResponse(200, GET_HOST_WWNS_RESULT)
                elif '/host-groups' in url:
                    return FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)
            else:
                if method == 'POST':
                    return FakeResponse(200, REMOTE_POST_SESSIONS_RESULT)
                elif '/ports' in url:
                    return FakeResponse(200, REMOTE_GET_PORTS_RESULT)
                elif '/host-wwns' in url:
                    return FakeResponse(200, REMOTE_GET_HOST_WWNS_RESULT)
                elif '/host-groups' in url:
                    return FakeResponse(
                        200, REMOTE_GET_HOST_GROUPS_RESULT_PAIR)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.do_setup(None)
        self.driver.check_for_setup_error()
        self.driver.local_path(None)
        self.driver.create_export(None, None, None)
        self.driver.ensure_export(None, None)
        self.driver.remove_export(None, None)
        self.driver.create_export_snapshot(None, None, None)
        self.driver.remove_export_snapshot(None, None)
        # stop the Loopingcall within the do_setup treatment
        self.driver.common.rep_primary.client.keep_session_loop.stop()
        self.driver.common.rep_secondary.client.keep_session_loop.stop()

    def tearDown(self):
        self.client = None
        super(HBSDREPLICATIONFCDriverTest, self).tearDown()

    # API test cases
    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(
        volume_utils, 'brick_get_connector_properties',
        side_effect=_brick_get_connector_properties)
    def test_do_setup(self, brick_get_connector_properties, request):
        drv = hbsd_fc.HBSDFCDriver(
            configuration=self.configuration)
        self._setup_config()

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method == 'POST':
                    return FakeResponse(200, POST_SESSIONS_RESULT)
                elif '/ports' in url:
                    return FakeResponse(200, GET_PORTS_RESULT)
                elif '/host-wwns' in url:
                    return FakeResponse(200, GET_HOST_WWNS_RESULT)
                elif '/host-groups' in url:
                    return FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)
            else:
                if method == 'POST':
                    return FakeResponse(200, REMOTE_POST_SESSIONS_RESULT)
                elif '/ports' in url:
                    return FakeResponse(200, REMOTE_GET_PORTS_RESULT)
                elif '/host-wwns' in url:
                    return FakeResponse(200, REMOTE_GET_HOST_WWNS_RESULT)
                elif '/host-groups' in url:
                    return FakeResponse(
                        200, REMOTE_GET_HOST_GROUPS_RESULT_PAIR)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        drv.do_setup(None)
        self.assertEqual(
            {CONFIG_MAP['port_id']: CONFIG_MAP['target_wwn']},
            drv.common.rep_primary.storage_info['wwns'])
        self.assertEqual(
            {REMOTE_CONFIG_MAP['port_id']: REMOTE_CONFIG_MAP['target_wwn']},
            drv.common.rep_secondary.storage_info['wwns'])
        self.assertEqual(2, brick_get_connector_properties.call_count)
        self.assertEqual(8, request.call_count)
        # stop the Loopingcall within the do_setup treatment
        drv.common.rep_primary.client.keep_session_loop.stop()
        drv.common.rep_secondary.client.keep_session_loop.stop()

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_create_volume(
            self, get_volume_type_extra_specs,
            get_volume_type_qos_specs, is_replicated, request):
        is_replicated.return_value = False
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        get_volume_type_extra_specs.return_value = {}
        request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_volume(TEST_VOLUME[8])
        actual = {'provider_location': json.dumps({'pldev': 1}),
                  'replication_status': fields.ReplicationStatus.DISABLED}
        self.assertEqual(actual, ret)
        self.assertEqual(2, request.call_count)
        args, kwargs = request.call_args
        self.assertNotIn(
            'Job-Mode-Wait-Configuration-Change', kwargs['headers'])

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_create_volume_replication(
            self, get_volume_type_qos_specs, is_replicated,
            get_volume_type_extra_specs, request):
        is_replicated.return_value = True
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        get_volume_type_extra_specs.return_value = {}

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url or
                            '/journals' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/remote-mirror-copypairs/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url or
                            '/journals' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_volume(TEST_VOLUME[8])
        actual = {
            'provider_location': json.dumps(
                {'pldev': 1, 'sldev': 2,
                 'remote-copy': hbsd_utils.REP_TYPE_ASYNC}),
            'replication_status': fields.ReplicationStatus.ENABLED}
        self.assertEqual(actual, ret)
        self.assertEqual(23, request.call_count)
        for args, kwargs in request.call_args_list:
            if args[0] == 'POST' and 'remote-mirror-copypairs' in args[1]:
                self.assertEqual('U', kwargs['json']['copyGroupName'][-3])
                self.assertEqual(
                    kwargs['headers']['Job-Mode-Wait-Configuration-Change'],
                    "NoWait")
                break
        else:
            self.fail('no create pair api')

    @mock.patch.object(requests.Session, "request")
    def test_delete_volume(self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        self.driver.delete_volume(TEST_VOLUME[0])
        self.assertEqual(5, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_delete_volume_replication(self, request):
        self.copygroup_count = 0
        self.ldev_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT', 'DELETE'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copygroups/' in url:
                        if self.copygroup_count < 2:
                            self.copygroup_count = self.copygroup_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYGROUP_RESULT)
                        else:
                            return FakeResponse(
                                500, GET_REMOTE_MIRROR_COPYGROUP_RESULT_ERROR,
                                headers={'Content-Type': 'json'})
                    elif '/remote-mirror-copypairs/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif '/ldevs/' in url:
                        if self.ldev_count == 0:
                            self.ldev_count = self.ldev_count + 1
                            return FakeResponse(200, GET_LDEV_RESULT_REP)
                        else:
                            return FakeResponse(200, GET_LDEV_RESULT_SPLIT)
                    elif '/journals/' in url:
                        return FakeResponse(200, GET_JOURNAL_RESULT)
            else:
                if method in ('POST', 'PUT', 'DELETE'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/journals/' in url:
                        return FakeResponse(200, REMOTE_GET_JOURNAL_RESULT)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_SPLIT)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.delete_volume(TEST_VOLUME[4])
        self.assertEqual(29, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_delete_volume_primary_is_invalid_ldev(self, request):
        request.return_value = FakeResponse(200, GET_LDEV_RESULT_LABEL)
        self.driver.delete_volume(TEST_VOLUME[0])
        self.assertEqual(1, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_delete_volume_primary_secondary_is_invalid_ldev(self, request):
        request.return_value = FakeResponse(200, GET_LDEV_RESULT_REP_LABEL)
        self.driver.delete_volume(TEST_VOLUME[4])
        self.assertEqual(2, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_delete_volume_secondary_is_invalid_ldev(self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_REP_LABEL),
                               FakeResponse(200, GET_LDEV_RESULT_REP),
                               FakeResponse(200, GET_LDEV_RESULT_REP),
                               FakeResponse(200, GET_LDEV_RESULT_REP),
                               FakeResponse(200, GET_LDEV_RESULT_REP),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        self.driver.delete_volume(TEST_VOLUME[4])
        self.assertEqual(6, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_extend_volume(self, get_volume_type_qos_specs, request):
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        self.driver.extend_volume(TEST_VOLUME[0], 256)
        self.assertEqual(6, request.call_count)
        body = request.call_args_list[5][1]['json']
        self.assertNotIn('enhancedExpansion', body['parameters'])

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_extend_volume_replication(
            self, get_volume_type_extra_specs, get_volume_type_qos_specs,
            request):
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        self.ldev_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT', 'DELETE'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copygroups/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUP_RESULT)
                    elif '/remote-mirror-copygroups' in url:
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/remote-mirror-copypairs/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif '/ldevs/' in url:
                        if self.ldev_count < 2:
                            self.ldev_count = self.ldev_count + 1
                            return FakeResponse(200, GET_LDEV_RESULT_REP)
                        else:
                            return FakeResponse(200, GET_LDEV_RESULT)
                    elif '/journals/' in url:
                        return FakeResponse(200, GET_JOURNAL_RESULT)
            else:
                if method in ('POST', 'PUT', 'DELETE'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/journals/' in url:
                        return FakeResponse(200, REMOTE_GET_JOURNAL_RESULT)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.extend_volume(TEST_VOLUME[4], 256)
        self.assertEqual(1, get_volume_type_extra_specs.call_count)
        self.assertEqual(27, request.call_count)
        body = request.call_args_list[14][1]['json']
        self.assertNotIn('enhancedExpansion', body['parameters'])
        body = request.call_args_list[19][1]['json']
        self.assertNotIn('enhancedExpansion', body['parameters'])
        for args, kwargs in request.call_args_list:
            if args[0] == 'POST' and 'remote-mirror-copypairs' in args[1]:
                self.assertEqual(
                    kwargs['headers']['Job-Mode-Wait-Configuration-Change'],
                    "NoWait")
                isDataReductionForceCopy = (
                    kwargs['json']['isDataReductionForceCopy'])
                break
        else:
            self.fail('no create pair api')
        self.assertFalse(isDataReductionForceCopy)

    @mock.patch.object(hbsd_common.HBSDCommon, "delete_pair")
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(requests.Session, "request")
    def test_extend_volume_enable_having_snapshots(
            self, request, get_volume_type_extra_specs,
            get_volume_type_qos_specs, delete_pair):
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        self.override_config('hitachi_extend_snapshot_volumes',
                             True, group=conf.SHARED_CONF_GROUP)
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        self.ldev_count = 0
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT', 'DELETE'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copygroups/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUP_RESULT)
                    elif '/remote-mirror-copygroups' in url:
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/remote-mirror-copypairs/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif '/ldevs/' in url:
                        if self.ldev_count < 2:
                            self.ldev_count = self.ldev_count + 1
                            return FakeResponse(200, GET_LDEV_RESULT_REP_PAIR)
                        else:
                            return FakeResponse(200, GET_LDEV_RESULT)
            else:
                if method in ('POST', 'PUT', 'DELETE'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.extend_volume(TEST_VOLUME[4], 256)
        self.assertEqual(24, request.call_count)
        body = request.call_args_list[12][1]['json']
        self.assertTrue(body['parameters']['enhancedExpansion'])
        body = request.call_args_list[16][1]['json']
        self.assertTrue(body['parameters']['enhancedExpansion'])

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_extend_pair_volume_capacity_saving_dedup_compression(
            self, get_volume_type_extra_specs, get_volume_type_qos_specs,
            request):
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True',
            'hbsd:capacity_saving': 'deduplication_compression'}
        self.ldev_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT', 'DELETE'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copygroups/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUP_RESULT)
                    elif '/remote-mirror-copygroups' in url:
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/remote-mirror-copypairs/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif '/ldevs/' in url:
                        if self.ldev_count < 2:
                            self.ldev_count = self.ldev_count + 1
                            return FakeResponse(200, GET_LDEV_RESULT_REP)
                        else:
                            return FakeResponse(200, GET_LDEV_RESULT)
                    elif '/journals/' in url:
                        return FakeResponse(200, GET_JOURNAL_RESULT)
            else:
                if method in ('POST', 'PUT', 'DELETE'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/journals/' in url:
                        return FakeResponse(200, REMOTE_GET_JOURNAL_RESULT)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.extend_volume(TEST_VOLUME[4], 256)
        self.assertEqual(1, get_volume_type_extra_specs.call_count)
        self.assertEqual(27, request.call_count)
        body = request.call_args_list[14][1]['json']
        self.assertNotIn('enhancedExpansion', body['parameters'])
        body = request.call_args_list[19][1]['json']
        self.assertNotIn('enhancedExpansion', body['parameters'])
        for args, kwargs in request.call_args_list:
            if args[0] == 'POST' and 'remote-mirror-copypairs' in args[1]:
                self.assertEqual(
                    kwargs['headers']['Job-Mode-Wait-Configuration-Change'],
                    "NoWait")
                isDataReductionForceCopy = (
                    kwargs['json']['isDataReductionForceCopy'])
                break
        else:
            self.fail('no create pair api')
        self.assertTrue(isDataReductionForceCopy)

    @mock.patch.object(driver.FibreChannelDriver, "get_goodness_function")
    @mock.patch.object(driver.FibreChannelDriver, "get_filter_function")
    @mock.patch.object(requests.Session, "request")
    def test_get_volume_stats(
            self, request, get_filter_function, get_goodness_function):
        request.return_value = FakeResponse(200, GET_POOLS_RESULT)
        get_filter_function.return_value = None
        get_goodness_function.return_value = None
        stats = self.driver.get_volume_stats(True)
        self.assertEqual('Hitachi', stats['vendor_name'])
        self.assertTrue(stats["pools"][0]['multiattach'])
        self.assertEqual(1, request.call_count)
        self.assertEqual(1, get_filter_function.call_count)
        self.assertEqual(1, get_goodness_function.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_create_snapshot(
            self, get_volume_type_qos_specs, volume_get,
            get_volume_type_extra_specs, request):
        get_volume_type_extra_specs.return_value = {}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
        actual = {'provider_location': json.dumps({'pldev': 1})}
        self.assertEqual(actual, ret)
        self.assertEqual(5, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_delete_snapshot(self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
                               FakeResponse(200, NOTFOUND_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        self.driver.delete_snapshot(TEST_SNAPSHOT[0])
        self.assertEqual(14, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch(
        'cinder.volume.drivers.hitachi.hbsd_rest.HBSDREST.'
        '_wait_copy_pair_status')
    def test_create_cloned_volume(
            self, mock_wait_copy_pair_status, get_volume_type_qos_specs,
            is_replicated, get_volume_type_extra_specs, get_volume_type,
            request):
        mock_wait_copy_pair_status.return_value = None
        is_replicated.return_value = False
        get_volume_type_extra_specs.return_value = {}
        get_volume_type.return_value = {}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if method == 'GET':
                if '/ldevs/' in url:
                    return FakeResponse(200, GET_LDEV_RESULT)
                elif '/snapshots' in url:
                    return FakeResponse(200, GET_SNAPSHOTS_RESULT)
                else:
                    return FakeResponse(200, NOTFOUND_RESULT)
            elif method in ('POST', 'PUT'):
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            elif method == 'DELETE':
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            return FakeResponse(500, ERROR_RESULT,
                                headers={'Content-Type': 'json'})

        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_cloned_volume(TEST_VOLUME[0], TEST_VOLUME[1])
        actual = {'provider_location': json.dumps({'pldev': 1}),
                  'replication_status': fields.ReplicationStatus.DISABLED}
        self.assertEqual(actual, ret)
        self.assertGreater(request.call_count, 0)  # Just verify requests
        # Check that the last call doesn't have
        # Job-Mode-Wait-Configuration-Change header
        if request.call_count > 0:
            args, kwargs = request.call_args_list[-1]
            if 'headers' in kwargs:
                self.assertNotIn(
                    'Job-Mode-Wait-Configuration-Change', kwargs['headers'])

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_create_cloned_volume_replication(
            self, get_volume_type_qos_specs, is_replicated,
            get_volume_type_extra_specs, get_volume_type, request):
        is_replicated.return_value = True
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        get_volume_type.return_value = {}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        self.snapshot_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url or
                            '/journals' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/remote-mirror-copypairs/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/snapshots' in url:
                        if self.snapshot_count < 1:
                            self.snapshot_count = self.snapshot_count + 1
                            return FakeResponse(200, GET_SNAPSHOTS_RESULT)
                        else:
                            return FakeResponse(200, NOTFOUND_RESULT)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url or
                            '/journals' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_cloned_volume(TEST_VOLUME[4], TEST_VOLUME[5])
        actual = {
            'provider_location': json.dumps(
                {'pldev': 1, 'sldev': 2,
                 'remote-copy': hbsd_utils.REP_TYPE_ASYNC}),
            'replication_status': fields.ReplicationStatus.ENABLED}
        self.assertEqual(actual, ret)
        self.assertEqual(2, get_volume_type_extra_specs.call_count)
        self.assertEqual(34, request.call_count)
        for args, kwargs in request.call_args_list:
            if args[0] == 'POST' and 'remote-mirror-copypairs' in args[1]:
                self.assertEqual(
                    kwargs['headers']['Job-Mode-Wait-Configuration-Change'],
                    "NoWait")
                isDataReductionForceCopy = (
                    kwargs['json']['isDataReductionForceCopy'])
                break
        else:
            self.fail('no create pair api')
        self.assertFalse(isDataReductionForceCopy)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch(
        'cinder.volume.drivers.hitachi.hbsd_rest.HBSDREST.'
        '_wait_copy_pair_status')
    def test_create_volume_from_snapshot(
            self, mock_wait_copy_pair_status, get_volume_type_qos_specs,
            is_replicated, get_volume_type_extra_specs, get_volume_type,
            request):
        mock_wait_copy_pair_status.return_value = None
        is_replicated.return_value = False
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        get_volume_type.return_value = {}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if method == 'GET':
                if '/ldevs/' in url:
                    return FakeResponse(200, GET_LDEV_RESULT)
                elif '/snapshots' in url:
                    return FakeResponse(200, GET_SNAPSHOTS_RESULT)
                else:
                    return FakeResponse(200, NOTFOUND_RESULT)
            elif method in ('POST', 'PUT'):
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            elif method == 'DELETE':
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            return FakeResponse(500, ERROR_RESULT,
                                headers={'Content-Type': 'json'})

        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_volume_from_snapshot(
            TEST_VOLUME[0], TEST_SNAPSHOT[0])
        actual = {'provider_location': json.dumps({'pldev': 1}),
                  'replication_status': fields.ReplicationStatus.DISABLED}
        self.assertEqual(actual, ret)
        self.assertEqual(2, get_volume_type_extra_specs.call_count)
        self.assertEqual(6, request.call_count)
        args, kwargs = request.call_args_list[5]
        self.assertNotIn(
            'Job-Mode-Wait-Configuration-Change', kwargs['headers'])

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch(
        'cinder.volume.drivers.hitachi.hbsd_rest.HBSDREST.'
        '_wait_copy_pair_status')
    @mock.patch(
        'cinder.volume.drivers.hitachi.hbsd_rest.HBSDREST.'
        '_wait_copy_pair_deleting')
    def test_create_volume_from_snapshot_replication(
            self, mock_wait_copy_pair_deleting, mock_wait_copy_pair_status,
            get_volume_type_qos_specs, is_replicated,
            get_volume_type_extra_specs, get_volume_type, request):
        mock_wait_copy_pair_deleting.return_value = None
        mock_wait_copy_pair_status.return_value = None
        is_replicated.return_value = True
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        get_volume_type.return_value = {}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if method == 'GET':
                if '/ldevs/' in url:
                    if '456789' in url:  # Remote storage
                        return FakeResponse(200, GET_LDEV_RESULT)
                    else:  # Primary storage
                        return FakeResponse(200, GET_LDEV_RESULT_PAIR)
                elif '/snapshots' in url:
                    # For delete_pair operations, return NOTFOUND to
                    # indicate no pairs
                    if params and 'pvolLdevId' in params:
                        return FakeResponse(200, NOTFOUND_RESULT)
                    else:
                        return FakeResponse(200, GET_SNAPSHOTS_RESULT)
                elif '/remote-mirror-copygroups/' in url:
                    return FakeResponse(
                        200,
                        GET_REMOTE_MIRROR_COPYGROUPS_RESULT_PAIR)
                elif '/remote-mirror-copypairs/' in url:
                    return FakeResponse(200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                elif '/journals' in url:
                    return FakeResponse(200, NOTFOUND_RESULT)
                else:
                    return FakeResponse(200, NOTFOUND_RESULT)
            elif method in ('POST', 'PUT'):
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            elif method == 'DELETE':
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            return FakeResponse(500, ERROR_RESULT,
                                headers={'Content-Type': 'json'})

        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_volume_from_snapshot(
            TEST_VOLUME[4], TEST_SNAPSHOT[4])
        actual = {
            'provider_location': json.dumps(
                {'pldev': 1, 'sldev': 1,
                 'remote-copy': hbsd_utils.REP_TYPE_ASYNC}),
            'replication_status': fields.ReplicationStatus.ENABLED}
        self.assertEqual(actual, ret)
        self.assertGreater(request.call_count, 0)  # Just verify requests
        # Check that one of the calls has the expected
        # Job-Mode-Wait-Configuration-Change header
        found_no_wait_header = False
        for args, kwargs in request.call_args_list:
            if ('headers' in kwargs and
                    'Job-Mode-Wait-Configuration-Change' in kwargs['headers']):
                if (kwargs['headers']['Job-Mode-Wait-Configuration-Change'] ==
                        "NoWait"):
                    found_no_wait_header = True
                    break
        self.assertTrue(
            found_no_wait_header,
            "Expected Job-Mode-Wait-Configuration-Change: NoWait header "
            "not found")

    @mock.patch.object(fczm_utils, "add_fc_zone")
    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_initialize_connection(
            self, get_volume_type_extra_specs, request, add_fc_zone):
        self.override_config('hitachi_zoning_request', True,
                             group=conf.SHARED_CONF_GROUP)
        self.driver.common.lookup_service = FakeLookupService()
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        request.side_effect = [FakeResponse(200, GET_HOST_WWNS_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        ret = self.driver.initialize_connection(
            TEST_VOLUME[0], DEFAULT_CONNECTOR)
        self.assertEqual('fibre_channel', ret['driver_volume_type'])
        self.assertEqual([CONFIG_MAP['target_wwn']], ret['data']['target_wwn'])
        self.assertEqual(1, ret['data']['target_lun'])
        self.assertEqual(1, get_volume_type_extra_specs.call_count)
        self.assertEqual(2, request.call_count)
        self.assertEqual(1, add_fc_zone.call_count)

    @mock.patch.object(fczm_utils, "remove_fc_zone")
    @mock.patch.object(requests.Session, "request")
    def test_terminate_connection(self, request, remove_fc_zone):
        self.override_config('hitachi_zoning_request', True,
                             group=conf.SHARED_CONF_GROUP)
        self.driver.common.lookup_service = FakeLookupService()
        request.side_effect = [FakeResponse(200, GET_HOST_WWNS_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT_MAPPED),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, NOTFOUND_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        self.driver.terminate_connection(TEST_VOLUME[2], DEFAULT_CONNECTOR)
        self.assertEqual(5, request.call_count)
        self.assertEqual(1, remove_fc_zone.call_count)

    @mock.patch.object(fczm_utils, "add_fc_zone")
    @mock.patch.object(requests.Session, "request")
    def test_initialize_connection_snapshot(self, request, add_fc_zone):
        self.override_config('hitachi_zoning_request', True,
                             group=conf.SHARED_CONF_GROUP)
        self.driver.common.lookup_service = FakeLookupService()
        request.side_effect = [FakeResponse(200, GET_HOST_WWNS_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        ret = self.driver.initialize_connection_snapshot(
            TEST_SNAPSHOT[0], DEFAULT_CONNECTOR)
        self.assertEqual('fibre_channel', ret['driver_volume_type'])
        self.assertEqual([CONFIG_MAP['target_wwn']], ret['data']['target_wwn'])
        self.assertEqual(1, ret['data']['target_lun'])
        self.assertEqual(2, request.call_count)
        self.assertEqual(1, add_fc_zone.call_count)

    @mock.patch.object(fczm_utils, "remove_fc_zone")
    @mock.patch.object(requests.Session, "request")
    def test_terminate_connection_snapshot(self, request, remove_fc_zone):
        self.override_config('hitachi_zoning_request', True,
                             group=conf.SHARED_CONF_GROUP)
        self.driver.common.lookup_service = FakeLookupService()
        request.side_effect = [FakeResponse(200, GET_HOST_WWNS_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT_MAPPED),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, NOTFOUND_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        self.driver.terminate_connection_snapshot(
            TEST_SNAPSHOT[0], DEFAULT_CONNECTOR)
        self.assertEqual(5, request.call_count)
        self.assertEqual(1, remove_fc_zone.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_manage_existing(
            self, get_volume_type_qos_specs, get_volume_type, request):
        get_volume_type.return_value = {}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, GET_LDEVS_RESULT)]
        ret = self.driver.manage_existing(
            TEST_VOLUME[0], self.test_existing_ref)
        actual = {'provider_location': json.dumps({'pldev': 1}),
                  'replication_status': fields.ReplicationStatus.DISABLED}
        self.assertEqual(actual, ret)
        self.assertEqual(0, get_volume_type.call_count)
        self.assertEqual(3, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(objects.Volume, 'is_replicated')
    def test_manage_existing_get_size(
            self, is_replicated, request):
        is_replicated.return_value = False
        request.return_value = FakeResponse(200, GET_LDEV_RESULT)
        self.driver.manage_existing_get_size(
            TEST_VOLUME[0], self.test_existing_ref)
        self.assertEqual(2, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_unmanage(self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT)]
        self.driver.unmanage(TEST_VOLUME[0])
        self.assertEqual(3, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_copy_image_to_volume(self, request):
        image_service = 'fake_image_service'
        image_id = 'fake_image_id'
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, COMPLETED_SUCCEEDED_RESULT)]
        with mock.patch.object(driver.VolumeDriver, 'copy_image_to_volume') \
                as mock_copy_image:
            self.driver.copy_image_to_volume(
                self.ctxt, TEST_VOLUME[0], image_service, image_id)
        mock_copy_image.assert_called_with(
            self.ctxt, TEST_VOLUME[0], image_service, image_id,
            disable_sparse=False)
        self.assertEqual(2, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_update_migrated_volume(self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, COMPLETED_SUCCEEDED_RESULT)]
        ret = self.driver.update_migrated_volume(
            self.ctxt, TEST_VOLUME[0], TEST_VOLUME[1], "available")
        self.assertEqual(2, request.call_count)
        actual = ({'_name_id': TEST_VOLUME[1]['id'],
                   'provider_location': TEST_VOLUME[1]['provider_location']})
        self.assertEqual(actual, ret)

    def test_unmanage_snapshot(self):
        """The driver don't support unmange_snapshot."""
        self.assertRaises(
            NotImplementedError,
            self.driver.unmanage_snapshot,
            TEST_SNAPSHOT[0])

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(obj_snap.SnapshotList, 'get_all_for_volume')
    @mock.patch.object(objects.VolumeType, 'is_replicated')
    def test_retype(self, is_replicated, get_all_for_volume,
                    get_volume_type_extra_specs, request):
        extra_specs = {'hbsd:test': 'test',
                       'hbsd:target_ports': 'CL2-A'}
        get_volume_type_extra_specs.return_value = extra_specs
        get_all_for_volume.return_value = True
        is_replicated.return_value = False

        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT)]

        old_specs = {'hbsd:target_ports': 'CL1-A'}
        new_specs = {'hbsd:target_ports': 'CL2-A'}
        old_type_ref = volume_types.create(self.ctxt, 'old', old_specs)
        new_type_ref = volume_types.create(self.ctxt, 'new', new_specs)
        new_type = objects.VolumeType.get_by_id(self.ctxt, new_type_ref['id'])

        diff = volume_types.volume_types_diff(self.ctxt, old_type_ref['id'],
                                              new_type_ref['id'])[0]
        host = {
            'capabilities': {
                'location_info': {
                    'pool_id': 30,
                },
            },
        }

        ret = self.driver.retype(
            self.ctxt, TEST_VOLUME[0], new_type, diff, host)
        self.assertEqual(2, request.call_count)
        self.assertFalse(ret)

    @mock.patch.object(requests.Session, "request")
    def test_retype_replication(self, request):
        extra_specs = {'test1': 'aaa'}

        request.return_value = FakeResponse(200, GET_LDEV_RESULT_REP)

        new_type_ref = volume_types.create(self.ctxt, 'new', extra_specs)
        new_type = objects.VolumeType.get_by_id(self.ctxt, new_type_ref['id'])
        diff = {}
        host = {
            'capabilities': {
                'location_info': {
                    'pool_id': 30,
                },
            },
        }
        ret = self.driver.retype(
            self.ctxt, TEST_VOLUME[0], new_type, diff, host)
        self.assertEqual(1, request.call_count)
        self.assertFalse(ret)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(objects.VolumeType, 'is_replicated')
    def test_retype_new_specs_replicated(self, is_replicated, request):
        is_replicated.return_value = True

        request.return_value = FakeResponse(200, GET_LDEV_RESULT)

        new_specs = {'replication_enabled': '<is> True'}
        new_type_ref = volume_types.create(self.ctxt, 'new', new_specs)
        new_type = objects.VolumeType.get_by_id(self.ctxt, new_type_ref['id'])
        diff = {}
        host = {
            'capabilities': {
                'location_info': {
                    'pool_id': 30,
                },
            },
        }
        ret = self.driver.retype(
            self.ctxt, TEST_VOLUME[0], new_type, diff, host)
        self.assertEqual(1, request.call_count)
        self.assertFalse(ret)

    @mock.patch.object(requests.Session, "request")
    def test_migrate_volume(
            self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT)]
        host = {
            'capabilities': {
                'location_info': {
                    'storage_id': CONFIG_MAP['serial'],
                    'pool_id': 30,
                    'execution_site': hbsd_utils.PRIMARY_STR,
                },
            },
        }
        ret = self.driver.migrate_volume(self.ctxt, TEST_VOLUME[0], host)
        self.assertEqual(3, request.call_count)
        actual = (True, None)
        self.assertTupleEqual(actual, ret)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_migrate_volume_diff_pool(
            self, get_volume_type_qos_specs,
            get_volume_type_extra_specs, request):
        extra_specs = {"test1": "aaa"}
        get_volume_type_extra_specs.return_value = extra_specs
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT_SMPL),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(200, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, COMPLETED_SUCCEEDED_RESULT)]
        host = {
            'capabilities': {
                'location_info': {
                    'storage_id': CONFIG_MAP['serial'],
                    'pool_id': 40,
                    'execution_site': hbsd_utils.PRIMARY_STR,
                },
            },
        }
        ret = self.driver.migrate_volume(self.ctxt, TEST_VOLUME[0], host)
        self.assertEqual(15, request.call_count)
        actual = (True,
                  {'provider_location': json.dumps({'pldev': 1}),
                   'replication_status': fields.ReplicationStatus.DISABLED})
        self.assertTupleEqual(actual, ret)

    @mock.patch.object(requests.Session, "request")
    def test_revert_to_snapshot(self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
                               FakeResponse(200, GET_LDEV_RESULT_PAIR),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT)]
        self.driver.revert_to_snapshot(
            self.ctxt, TEST_VOLUME[0], TEST_SNAPSHOT[0])
        self.assertEqual(6, request.call_count)
        args, kwargs = request.call_args_list[4]
        self.assertNotIn(
            'Job-Mode-Wait-Configuration-Change', kwargs['headers'])

    def test_create_group(self):
        ret = self.driver.create_group(self.ctxt, TEST_GROUP[0])
        self.assertIsNone(ret)

    @mock.patch.object(requests.Session, "request")
    def test_delete_group(self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        ret = self.driver.delete_group(
            self.ctxt, TEST_GROUP[0], [TEST_VOLUME[0]])
        self.assertEqual(4, request.call_count)
        actual = (
            {'status': TEST_GROUP[0]['status']},
            [{'id': TEST_VOLUME[0]['id'], 'status': 'deleted'}]
        )
        self.assertTupleEqual(actual, ret)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_create_group_from_src_volume(
            self, get_volume_type_qos_specs, get_volume_type_extra_specs,
            get_volume_type, request):
        get_volume_type_extra_specs.return_value = {}
        get_volume_type.return_value = {}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if method == 'GET':
                if '/ldevs/' in url:
                    return FakeResponse(200, GET_LDEV_RESULT)
                elif '/snapshots' in url:
                    return FakeResponse(200, GET_SNAPSHOTS_RESULT)
                else:
                    return FakeResponse(200, NOTFOUND_RESULT)
            elif method in ('POST', 'PUT'):
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            elif method == 'DELETE':
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            return FakeResponse(500, ERROR_RESULT,
                                headers={'Content-Type': 'json'})

        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_group_from_src(
            self.ctxt, TEST_GROUP[1], [TEST_VOLUME[1]],
            source_group=TEST_GROUP[0], source_vols=[TEST_VOLUME[0]]
        )
        self.assertGreater(request.call_count, 0)
        actual = (
            None,
            [{'id': TEST_VOLUME[1]['id'],
              'provider_location': json.dumps({'pldev': 1}),
              'replication_status': fields.ReplicationStatus.DISABLED}])
        self.assertTupleEqual(actual, ret)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch(
        'cinder.volume.drivers.hitachi.hbsd_rest.HBSDREST.'
        '_wait_copy_pair_status')
    def test_create_group_from_src_snapshot(
            self, mock_wait_copy_pair_status, get_volume_type_qos_specs,
            get_volume_type_extra_specs, get_volume_type, request):
        mock_wait_copy_pair_status.return_value = None
        get_volume_type_extra_specs.return_value = {}
        get_volume_type.return_value = {}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if method == 'GET':
                if '/ldevs/' in url:
                    return FakeResponse(200, GET_LDEV_RESULT)
                elif '/snapshots' in url:
                    return FakeResponse(200, GET_SNAPSHOTS_RESULT)
                else:
                    return FakeResponse(200, NOTFOUND_RESULT)
            elif method in ('POST', 'PUT'):
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            elif method == 'DELETE':
                return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
            return FakeResponse(500, ERROR_RESULT,
                                headers={'Content-Type': 'json'})

        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_group_from_src(
            self.ctxt, TEST_GROUP[0], [TEST_VOLUME[0]],
            group_snapshot=TEST_GROUP_SNAP[0], snapshots=[TEST_SNAPSHOT[0]]
        )
        self.assertGreater(request.call_count, 0)
        actual = (
            None,
            [{'id': TEST_VOLUME[0]['id'],
              'provider_location': json.dumps({'pldev': 1}),
              'replication_status': fields.ReplicationStatus.DISABLED}])
        self.assertTupleEqual(actual, ret)

    @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
    @mock.patch.object(requests.Session, "request")
    def test_update_group(self, request, is_group_a_cg_snapshot_type):
        request.return_value = FakeResponse(200, GET_LDEV_RESULT)
        is_group_a_cg_snapshot_type.return_value = False
        ret = self.driver.update_group(
            self.ctxt, TEST_GROUP[0], add_volumes=[TEST_VOLUME[0]])
        self.assertTupleEqual((None, None, None), ret)
        self.assertEqual(1, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
    @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_create_group_snapshot_non_cg(
            self, get_volume_type_qos_specs, is_group_a_cg_snapshot_type,
            volume_get, get_volume_type_extra_specs, request):
        is_group_a_cg_snapshot_type.return_value = False
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_group_snapshot(
            self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
        )
        self.assertEqual(5, request.call_count)
        actual = (
            {'status': 'available'},
            [{'id': TEST_SNAPSHOT[0]['id'],
              'provider_location': json.dumps({'pldev': 1}),
              'status': 'available'}]
        )
        self.assertTupleEqual(actual, ret)

    @mock.patch.object(requests.Session, "request")
    def test_delete_group_snapshot(self, request):
        request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
                               FakeResponse(200, NOTFOUND_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(200, GET_SNAPSHOTS_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(200, GET_LDEV_RESULT),
                               FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
        ret = self.driver.delete_group_snapshot(
            self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]])
        self.assertEqual(14, request.call_count)
        actual = (
            {'status': TEST_GROUP_SNAP[0]['status']},
            [{'id': TEST_SNAPSHOT[0]['id'], 'status': 'deleted'}]
        )
        self.assertTupleEqual(actual, ret)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_failover_host(self, get_volume_type_extra_specs, request):
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count < 1:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                        else:
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        ret = self.driver.failover_host(
            self.ctxt, [TEST_VOLUME[5]],
            secondary_id=
            self.configuration.replication_device[0]['backend_id'])
        actual = (
            self.configuration.replication_device[0]['backend_id'],
            [{'volume_id': TEST_VOLUME[5]['id'],
              'updates': {
                  'replication_status':
                      fields.ReplicationStatus.FAILED_OVER}}],
            [])
        self.assertTupleEqual(actual, ret)
        self.assertEqual(1, get_volume_type_extra_specs.call_count)
        self.assertEqual(4, request.call_count)
        self.assertEqual(
            self.driver.common.rep_secondary.backend_id,
            self.driver.common._active_backend_id)
        self.driver.common._active_backend_id = ''
        for args, kwargs in request.call_args_list:
            if args[0] == 'PUT' and 'remote-mirror-copypairs' in args[1]:
                self.assertEqual(
                    kwargs['headers']['Job-Mode-Wait-Configuration-Change'],
                    "NoWait")
                break
        else:
            self.fail('no swap pair api')

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(
        volume_utils, 'brick_get_connector_properties',
        side_effect=_brick_get_connector_properties)
    def test_failover_host_failback(
            self, brick_get_connector_properties, request):
        self.driver.common._active_backend_id = \
            self.driver.common.rep_secondary.backend_id
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    if '/sessions' in url:
                        return FakeResponse(200, POST_SESSIONS_RESULT)
                    else:
                        return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count == 0:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SPLIT)
                        elif self.copypair_count == 2:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_PSUS)
                        else:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif ('/remote-mirror-copygroups/' in url):
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUP_RESULT_SSWS)
                    elif ('/remote-mirror-copygroups' in url):
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUPS_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, GET_PORTS_RESULT)
                    elif '/host-wwns' in url:
                        return FakeResponse(200, GET_HOST_WWNS_RESULT)
                    elif '/host-groups' in url:
                        return FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)
            else:
                if method in ('POST', 'PUT'):
                    if '/sessions' in url:
                        return FakeResponse(200, REMOTE_POST_SESSIONS_RESULT)
                    else:
                        return FakeResponse(
                            202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, REMOTE_GET_PORTS_RESULT)
                    elif '/host-wwns' in url:
                        return FakeResponse(200, REMOTE_GET_HOST_WWNS_RESULT)
                    elif '/host-groups' in url:
                        return FakeResponse(
                            200, REMOTE_GET_HOST_GROUPS_RESULT_PAIR)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        ret = self.driver.failover_host(
            self.ctxt, [TEST_VOLUME[6]],
            secondary_id=hbsd_replication._REP_FAILBACK)
        actual = (
            hbsd_replication._REP_FAILBACK,
            [{'volume_id': TEST_VOLUME[6]['id'],
              'updates': {
                  'replication_status':
                      fields.ReplicationStatus.ENABLED}}],
            [])
        self.assertTupleEqual(actual, ret)
        self.assertEqual(13, request.call_count)
        self.assertEqual('', self.driver.common._active_backend_id)
        for args, kwargs in request.call_args_list:
            if args[0] == 'PUT' and 'remote-mirror-copygroups' in args[1]:
                self.assertEqual(
                    kwargs['headers']['Job-Mode-Wait-Configuration-Change'],
                    "NoWait")
                break
        else:
            self.fail('no resync pair api')

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_failover(self, get_volume_type_extra_specs, request):
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True'}
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count < 1:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                        else:
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        ret = self.driver.failover(
            self.ctxt, [TEST_VOLUME[5]],
            secondary_id=
            self.configuration.replication_device[0]['backend_id'])
        actual = (
            self.configuration.replication_device[0]['backend_id'],
            [{'volume_id': TEST_VOLUME[5]['id'],
              'updates': {
                  'replication_status':
                      fields.ReplicationStatus.FAILED_OVER}}],
            [])
        self.assertTupleEqual(actual, ret)
        self.assertEqual(1, get_volume_type_extra_specs.call_count)
        self.assertEqual(4, request.call_count)
        for args, kwargs in request.call_args_list:
            if args[0] == 'PUT' and 'remote-mirror-copypairs' in args[1]:
                self.assertEqual(
                    kwargs['headers']['Job-Mode-Wait-Configuration-Change'],
                    "NoWait")
                break
        else:
            self.fail('no swap pair api')

    def test_failover_completed(self):
        self.driver.failover_completed(
            self.ctxt,
            active_backend_id=
            self.configuration.replication_device[0]['backend_id'])
        self.assertEqual(
            self.driver.common.rep_secondary.backend_id,
            self.driver.common._active_backend_id)
        self.driver.common._active_backend_id = ''

    @mock.patch.object(requests.Session, "request")
    def test_get_ldev_site_sldev_in_loc(self, request):
        self.assertRaises(exception.VolumeDriverException,
                          self.driver.delete_snapshot,
                          TEST_SNAPSHOT[5])
        self.assertEqual(0, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_get_ldev_site_pldev_in_loc(self, request):
        self.assertRaises(exception.VolumeDriverException,
                          self.driver.delete_snapshot,
                          TEST_SNAPSHOT[3])
        self.assertEqual(1, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_verify_ldev_have_active_backend_id(self, request):
        self.driver.common._active_backend_id = 'test'
        self.assertRaises(exception.VolumeDriverException,
                          self.driver.delete_snapshot,
                          TEST_SNAPSHOT[3])
        self.assertEqual(0, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_create_journals_continue(
            self, get_volume_type_extra_specs, get_volume_type_qos_specs,
            is_replicated, request):
        is_replicated.return_value = True
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        get_volume_type_extra_specs.return_value = {}

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            req_count = 0
            # for_use_global_scope_counter
            global global_counter
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    if '/journals' in url:
                        if global_counter == 0:
                            req_count += 1
                            global_counter += 1
                            return FakeResponse(
                                200, GET_JOURNAL_RESULT_MESSAGEID_KART40054E,
                                headers={'Content-Type': 'json'})
                        else:
                            return FakeResponse(
                                200, GET_JOURNAL_RESULT,
                                headers={'Content-Type': 'json'})
                    else:
                        return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/journals' in url:
                        return FakeResponse(
                            200, GET_JOURNAL_RESULT_MESSAGEID_KART40046E,
                            headers={'Content-Type': 'json'})
                    elif '/remote-mirror-copypairs/' in url:
                        # reset_global_counter_of_the_end_of_request
                        global_counter = 0
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url or
                            '/journals' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
            return FakeResponse(
                400, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        ret = self.driver.create_volume(TEST_VOLUME[8])
        actual = {
            'provider_location': json.dumps(
                {'pldev': 1, 'sldev': 2,
                 'remote-copy': hbsd_utils.REP_TYPE_ASYNC}),
            'replication_status': fields.ReplicationStatus.ENABLED}
        self.assertEqual(actual, ret)
        self.assertEqual(25, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_create_journals_raise_error(
            self, get_volume_type_extra_specs,
            get_volume_type_qos_specs, is_replicated, request):
        is_replicated.return_value = True
        get_volume_type_extra_specs.return_value = {}

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    if '/journals' in url:
                        return FakeResponse(
                            200, GET_JOURNAL_RESULT_MESSAGEID_KART40046E,
                            headers={'Content-Type': 'json'})
                    else:
                        return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/journals' in url:
                        return FakeResponse(
                            200, GET_JOURNAL_RESULT_MESSAGEID_KART40054E,
                            headers={'Content-Type': 'json'})
                    elif '/remote-mirror-copypairs/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url or
                            '/journals' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
            return FakeResponse(
                400, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        self.assertRaises(exception.VolumeDriverException,
                          self.driver.create_volume,
                          TEST_VOLUME[8])
        self.assertEqual(13, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(
        volume_utils, 'brick_get_connector_properties',
        side_effect=_brick_get_connector_properties)
    def test_get_failback_volume_update_snapshot_error(
            self, brick_get_connector_properties, request):
        self.driver.common._active_backend_id = \
            self.driver.common.rep_secondary.backend_id
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    if '/sessions' in url:
                        return FakeResponse(200, POST_SESSIONS_RESULT)
                    else:
                        return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count == 0:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SPLIT)
                        elif self.copypair_count == 2:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_PSUS)
                        else:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif ('/remote-mirror-copygroups/' in url):
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUP_RESULT_SSWS)
                    elif ('/remote-mirror-copygroups' in url):
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUPS_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, GET_PORTS_RESULT)
                    elif '/host-wwns' in url:
                        return FakeResponse(200, GET_HOST_WWNS_RESULT)
                    elif '/host-groups' in url:
                        return FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)
            else:
                if method in ('POST', 'PUT'):
                    if '/sessions' in url:
                        return FakeResponse(200, REMOTE_POST_SESSIONS_RESULT)
                    else:
                        return FakeResponse(
                            202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, REMOTE_GET_PORTS_RESULT)
                    elif '/host-wwns' in url:
                        return FakeResponse(200, REMOTE_GET_HOST_WWNS_RESULT)
                    elif '/host-groups' in url:
                        return FakeResponse(
                            200, REMOTE_GET_HOST_GROUPS_RESULT_PAIR)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.test_vol_add = TEST_VOLUME[6]
        attrs = {'objects': [TEST_SNAPSHOT[5]]}
        snaplist_obj = objects.SnapshotList(CTXT, **attrs)
        self.test_vol_add['snapshots'] = snaplist_obj
        self.assertRaises(exception.SnapshotNotFound,
                          self.driver.failover_host,
                          self.ctxt,
                          [self.test_vol_add],
                          secondary_id=hbsd_replication._REP_FAILBACK)
        self.assertEqual(13, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(
        volume_utils, 'brick_get_connector_properties',
        side_effect=_brick_get_connector_properties)
    def test_failback_copy_group_except(
            self, brick_get_connector_properties, request):
        self.driver.common._active_backend_id = \
            self.driver.common.rep_secondary.backend_id
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    if '/sessions' in url:
                        return FakeResponse(200, POST_SESSIONS_RESULT)
                    else:
                        return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count == 0:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SPLIT)
                        elif self.copypair_count == 2:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_PSUS)
                        else:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                500, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif ('/remote-mirror-copygroups/' in url):
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUP_RESULT_SSWS)
                    elif ('/remote-mirror-copygroups' in url):
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUPS_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, GET_PORTS_RESULT)
                    elif '/host-wwns' in url:
                        return FakeResponse(200, GET_HOST_WWNS_RESULT)
                    elif '/host-groups' in url:
                        return FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)
            else:
                if method in ('POST', 'PUT'):
                    if '/sessions' in url:
                        return FakeResponse(200, REMOTE_POST_SESSIONS_RESULT)
                    else:
                        return FakeResponse(
                            202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, REMOTE_GET_PORTS_RESULT)
                    elif '/host-wwns' in url:
                        return FakeResponse(200, REMOTE_GET_HOST_WWNS_RESULT)
                    elif '/host-groups' in url:
                        return FakeResponse(
                            200, REMOTE_GET_HOST_GROUPS_RESULT_PAIR)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.failover_host(
            self.ctxt, [TEST_VOLUME[6]],
            secondary_id=hbsd_replication._REP_FAILBACK)
        self.assertEqual(10, request.call_count)
        self.assertEqual('', self.driver.common._active_backend_id)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(
        volume_utils, 'brick_get_connector_properties',
        side_effect=_brick_get_connector_properties)
    def test_failback_volume_raise_exception(
            self, brick_get_connector_properties, request):
        self.driver.common._active_backend_id = \
            self.driver.common.rep_secondary.backend_id
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    if '/sessions' in url:
                        return FakeResponse(200, POST_SESSIONS_RESULT)
                    else:
                        return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count == 0:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SPLIT)
                        elif self.copypair_count == 2:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_PSUS)
                        else:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                500, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif ('/remote-mirror-copygroups/' in url):
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYGROUP_RESULT_SSWS)
                    elif ('/remote-mirror-copygroups' in url):
                        return FakeResponse(
                            400, GET_REMOTE_MIRROR_COPYGROUPS_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, GET_PORTS_RESULT)
                    elif '/host-wwns' in url:
                        return FakeResponse(200, GET_HOST_WWNS_RESULT)
                    elif '/host-groups' in url:
                        return FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)
            else:
                if method in ('POST', 'PUT'):
                    if '/sessions' in url:
                        return FakeResponse(200, REMOTE_POST_SESSIONS_RESULT)
                    else:
                        return FakeResponse(
                            202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, REMOTE_GET_PORTS_RESULT)
                    elif '/host-wwns' in url:
                        return FakeResponse(200, REMOTE_GET_HOST_WWNS_RESULT)
                    elif '/host-groups' in url:
                        return FakeResponse(
                            200, REMOTE_GET_HOST_GROUPS_RESULT_PAIR)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.assertRaises(exception.UnableToFailOver,
                          self.driver.failover_host,
                          self.ctxt,
                          [TEST_VOLUME[6]],
                          secondary_id=hbsd_replication._REP_FAILBACK)
        self.assertEqual(4, request.call_count)

    def test_failover_pair_volume_pldev_or_sldev_is_none(self):
        self.copypair_count = 0
        self.driver.failover(
            self.ctxt, [TEST_VOLUME[7]],
            secondary_id=
            self.configuration.replication_device[0]['backend_id'])

    @mock.patch.object(requests.Session, "request")
    def test_failover_pair_volume_not_pair_info(self, request):
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count < 1:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                        else:
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.failover(
            self.ctxt, [TEST_VOLUME[5]],
            secondary_id=
            self.configuration.replication_device[0]['backend_id'])
        self.assertEqual(1, request.call_count)

    @mock.patch.object(requests.Session, "request")
    def test_failover_pair_volume_svol_status_not_pair(self, request):
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count < 1:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS)
                        else:
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.failover(
            self.ctxt, [TEST_VOLUME[5]],
            secondary_id=
            self.configuration.replication_device[0]['backend_id'])
        self.assertEqual(2, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_failover_volume_except(
            self, get_volume_type_extra_specs, request):
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True',
            'replication_type': "async"}
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count < 1:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, NOTFOUND_RESULT)
                        else:
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(400, GET_LDEV_RESULT_REP)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.driver.failover(
            self.ctxt, [TEST_VOLUME[5]],
            secondary_id=
            self.configuration.replication_device[0]['backend_id'])
        self.assertEqual(0, get_volume_type_extra_specs.call_count)
        self.assertEqual(1, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_failover_raise_exception_IRT(
            self, get_volume_type_extra_specs, request):
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True',
            'replication_type': "split test"}
        self.copypair_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count < 1:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                        else:
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.assertRaises(exception.InvalidReplicationTarget,
                          self.driver.failover,
                          self.ctxt,
                          [TEST_VOLUME[2]],
                          secondary_id='test')
        self.assertEqual(0, get_volume_type_extra_specs.call_count)
        self.assertEqual(0, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    def test_failover_raise_exception_UTF(
            self, get_volume_type_extra_specs, request):
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True',
            'replication_type': "split test"}
        self.copypair_count = 0
        self.driver.common._active_backend_id = 'test'

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/ports' in url:
                        return FakeResponse(200, GET_PORTS_RESULT)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if '/remote-mirror-copypairs/' in url:
                        if self.copypair_count < 1:
                            self.copypair_count = self.copypair_count + 1
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                        else:
                            return FakeResponse(
                                200, GET_REMOTE_MIRROR_COPYPAIR_RESULT_SSWS)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            return FakeResponse(
                500, ERROR_RESULT, headers={'Content-Type': 'json'})
        request.side_effect = _request_side_effect
        self.assertRaises(exception.UnableToFailOver,
                          self.driver.failover,
                          self.ctxt,
                          [TEST_VOLUME[2]],
                          secondary_id='default')
        self.assertEqual(0, get_volume_type_extra_specs.call_count)
        self.assertEqual(1, request.call_count)

    def test_update_group_ldev_is_none(self):
        self.assertRaises(TypeError,
                          self.driver.update_group,
                          self.ctxt,
                          TEST_GROUP[0],
                          add_volumes=[TEST_VOLUME[3]])

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type')
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
    def test_update_group_has_rep_pair_true(
            self, is_group_a_cg_snapshot_type,
            get_volume_type_extra_specs, get_volume_type, request):
        self.driver.common._active_backend_id = 'test'
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True',
            'hbsd:capacity_saving': 'deduplication_compression'}
        get_volume_type.return_value = {}
        request.return_value = FakeResponse(200, GET_LDEV_RESULT_REP)
        is_group_a_cg_snapshot_type.return_value = False
        self.assertRaises(TypeError,
                          self.driver.update_group,
                          self.ctxt,
                          TEST_GROUP[0],
                          add_volumes=[TEST_VOLUME[5]])
        self.assertEqual(1, request.call_count)

    @mock.patch.object(requests.Session, "request")
    @mock.patch.object(volume_types, 'get_volume_type_extra_specs')
    @mock.patch.object(objects.Volume, 'is_replicated')
    @mock.patch.object(volume_types, 'get_volume_type_qos_specs')
    def test_create_rep_ldev_and_pair_capacity_saving_dedup_compression(
            self, get_volume_type_qos_specs, is_replicated,
            get_volume_type_extra_specs, request):
        self.driver.common.rep_primary._stats = {}
        self.driver.common.rep_primary._stats['pools'] = [
            {'location_info': {'pool_id': 30}}]
        self.driver.common.rep_secondary._stats = {}
        self.driver.common.rep_secondary._stats['pools'] = [
            {'location_info': {'pool_id': 40}}]
        is_replicated.return_value = True
        get_volume_type_extra_specs.return_value = {
            'replication_enabled': '<is> True',
            'hbsd:capacity_saving': 'deduplication_compression'}
        get_volume_type_qos_specs.return_value = {'qos_specs': None}
        self.snapshot_count = 0

        def _request_side_effect(
                method, url, params, json, headers, auth, timeout, verify):
            if self.configuration.hitachi_storage_id in url:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url or
                            '/journals' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/remote-mirror-copypairs/' in url:
                        return FakeResponse(
                            200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
                    elif '/snapshots' in url:
                        if self.snapshot_count < 1:
                            self.snapshot_count = self.snapshot_count + 1
                            return FakeResponse(200, GET_SNAPSHOTS_RESULT)
                        else:
                            return FakeResponse(200, NOTFOUND_RESULT)
            else:
                if method in ('POST', 'PUT'):
                    return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
                elif method == 'GET':
                    if ('/remote-mirror-copygroups' in url or
                            '/journals' in url):
                        return FakeResponse(200, NOTFOUND_RESULT)
                    elif '/ldevs/' in url:
                        return FakeResponse(200, GET_LDEV_RESULT_REP)
            if '/ldevs/' in url:
                return FakeResponse(200, GET_LDEV_RESULT_REP)
            else:
                return FakeResponse(
                    200, COMPLETED_SUCCEEDED_RESULT)
        request.side_effect = _request_side_effect
        ret = self.driver.create_cloned_volume(
            TEST_VOLUME[5], TEST_VOLUME[6])
        actual = {
            'provider_location': json.dumps(
                {'pldev': 1, 'sldev': 1,
                 'remote-copy': hbsd_utils.REP_TYPE_ASYNC}),
            'replication_status': fields.ReplicationStatus.ENABLED}
        self.assertEqual(actual, ret)
        self.assertEqual(2, get_volume_type_extra_specs.call_count)
        self.assertEqual(34, request.call_count)
        isDataReductionForceCopy = None
        for (args, kwargs) in request.call_args_list:
            if (args[0] == 'POST' and
                    '/remote-mirror-copypairs' in args[1]):
                isDataReductionForceCopy = (
                    kwargs['json']['isDataReductionForceCopy'])
                break
        else:
            self.fail('no create pair request')
        self.assertTrue(isDataReductionForceCopy)
