cinder/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py

1631 lines
72 KiB
Python

# Copyright (C) 2020, 2023, Hitachi, Ltd.
#
# 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 functools
from unittest import mock
from oslo_config import cfg
from oslo_utils import units
import requests
from requests import models
from cinder import context as cinder_context
from cinder.db.sqlalchemy import api as sqlalchemy_api
from cinder import exception
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.tests.unit import utils as test_utils
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 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', '0123456789abcdeg'],
'target_wwn': '1111111123456789',
'user_id': 'user',
'user_pass': 'password',
'pool_name': 'test_pool',
'auth_user': 'auth_user',
'auth_password': 'auth_password',
}
# Dummy response for FC zoning device mapping
DEVICE_MAP = {
'fabric_name': {
'initiator_port_wwn_list': [CONFIG_MAP['host_wwn'][0]],
'target_port_wwn_list': [CONFIG_MAP['target_wwn']]}}
DEFAULT_CONNECTOR = {
'host': 'host',
'ip': CONFIG_MAP['my_ip'],
'wwpns': [CONFIG_MAP['host_wwn'][0]],
'multipath': False,
}
DEVICE_MAP_MULTI_WWN = {
'fabric_name': {
'initiator_port_wwn_list': [
CONFIG_MAP['host_wwn'][0],
CONFIG_MAP['host_wwn'][1]
],
'target_port_wwn_list': [CONFIG_MAP['target_wwn']]}}
DEFAULT_CONNECTOR_MULTI_WWN = {
'host': 'host',
'ip': CONFIG_MAP['my_ip'],
'wwpns': [CONFIG_MAP['host_wwn'][0], CONFIG_MAP['host_wwn'][1]],
'multipath': False,
}
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
for i in range(4):
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:
volume['provider_location'] = None
else:
volume['provider_location'] = '{0:d}'.format(i)
volume['size'] = 128
if i == 2:
volume['status'] = 'in-use'
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 = []
snapshot = {}
snapshot['id'] = '10000000-0000-0000-0000-{0:012d}'.format(0)
snapshot['name'] = 'TEST_SNAPSHOT{0:d}'.format(0)
snapshot['provider_location'] = '{0:d}'.format(1)
snapshot['status'] = 'available'
snapshot['volume_id'] = '00000000-0000-0000-0000-{0:012d}'.format(0)
snapshot['volume'] = _volume_get(None, snapshot['volume_id'])
snapshot['volume_name'] = 'test-volume{0:d}'.format(0)
snapshot['volume_size'] = 128
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,
}
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'],
},
],
}
GET_HOST_WWNS_RESULT = {
"data": [
{
"hostGroupNumber": 0,
"hostWwn": CONFIG_MAP['host_wwn'][0],
},
],
}
GET_HOST_GROUPS_RESULT_TEST = {
"data": [
{
"hostGroupNumber": 0,
"portId": CONFIG_MAP['port_id'],
"hostGroupName": CONFIG_MAP['host_grp_name'],
},
],
}
COMPLETED_SUCCEEDED_RESULT = {
"status": "Completed",
"state": "Succeeded",
"affectedResources": ('a/b/c/1',),
}
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",
}
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",
}
GET_LDEV_RESULT_PAIR_TEST = {
"emulationType": "OPEN-V-CVS",
"blockCapacity": 2097152,
"attributes": ["CVS", "HDP", "HTI", "111"],
"status": "NML",
"snapshotPoolId": 0
}
GET_LDEV_RESULT_PAIR_STATUS_TEST = {
"emulationType": "OPEN-V-CVS",
"blockCapacity": 2097152,
"attributes": ["CVS", "HDP", "HTI"],
"status": "TEST",
"poolId": 30,
"dataReductionStatus": "REHYDRATING",
"dataReductionMode": "disabled"
}
GET_POOL_RESULT = {
"availableVolumeCapacity": 480144,
"totalPoolCapacity": 507780,
"totalLocatedCapacity": 71453172,
}
GET_SNAPSHOTS_RESULT = {
"data": [
{
"primaryOrSecondary": "S-VOL",
"status": "PSUS",
"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_POOLS_RESULT = {
"data": [
{
"poolId": 30,
"poolName": CONFIG_MAP['pool_name'],
"availableVolumeCapacity": 480144,
"totalPoolCapacity": 507780,
"totalLocatedCapacity": 71453172,
"virtualVolumeCapacityRate": -1,
},
],
}
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",
},
],
}
GET_LDEVS_RESULT = {
"data": [
{
"ldevId": 0,
"label": "15960cc738c94c5bb4f1365be5eeed44",
},
{
"ldevId": 1,
"label": "15960cc738c94c5bb4f1365be5eeed45",
},
],
}
NOTFOUND_RESULT = {
"data": [],
}
ERROR_RESULT = {
"errorSource": "<URL>",
"message": "<message>",
"solution": "<solution>",
"messageId": "<messageId>",
"errorCode": {
"SSB1": "",
"SSB2": "",
}
}
def _brick_get_connector_properties(multipath=False, enforce_multipath=False):
"""Return a predefined connector object."""
return DEFAULT_CONNECTOR
def _brick_get_connector_properties_multi_wwn(
multipath=False, enforce_multipath=False):
"""Return a predefined connector object."""
return DEFAULT_CONNECTOR_MULTI_WWN
def reduce_retrying_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
backup_lock_waittime = hbsd_rest_api._LOCK_TIMEOUT
backup_exec_max_waittime = hbsd_rest_api._REST_TIMEOUT
backup_job_api_response_timeout = (
hbsd_rest_api._JOB_API_RESPONSE_TIMEOUT)
backup_get_api_response_timeout = (
hbsd_rest_api._GET_API_RESPONSE_TIMEOUT)
backup_extend_waittime = hbsd_rest_api._EXTEND_TIMEOUT
backup_exec_retry_interval = hbsd_rest_api._EXEC_RETRY_INTERVAL
backup_rest_server_restart_timeout = (
hbsd_rest_api._REST_SERVER_RESTART_TIMEOUT)
backup_state_transition_timeout = (
hbsd_rest._STATE_TRANSITION_TIMEOUT)
hbsd_rest_api._LOCK_TIMEOUT = 0.01
hbsd_rest_api._REST_TIMEOUT = 0.01
hbsd_rest_api._JOB_API_RESPONSE_TIMEOUT = 0.01
hbsd_rest_api._GET_API_RESPONSE_TIMEOUT = 0.01
hbsd_rest_api._EXTEND_TIMEOUT = 0.01
hbsd_rest_api._EXEC_RETRY_INTERVAL = 0.004
hbsd_rest_api._REST_SERVER_RESTART_TIMEOUT = 0.02
hbsd_rest._STATE_TRANSITION_TIMEOUT = 0.01
func(*args, **kwargs)
hbsd_rest_api._LOCK_TIMEOUT = backup_lock_waittime
hbsd_rest_api._REST_TIMEOUT = backup_exec_max_waittime
hbsd_rest_api._JOB_API_RESPONSE_TIMEOUT = (
backup_job_api_response_timeout)
hbsd_rest_api._GET_API_RESPONSE_TIMEOUT = (
backup_get_api_response_timeout)
hbsd_rest_api._EXTEND_TIMEOUT = backup_extend_waittime
hbsd_rest_api._EXEC_RETRY_INTERVAL = backup_exec_retry_interval
hbsd_rest_api._REST_SERVER_RESTART_TIMEOUT = (
backup_rest_server_restart_timeout)
hbsd_rest._STATE_TRANSITION_TIMEOUT = (
backup_state_transition_timeout)
return wrapper
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 FakeLookupServiceMultiWwn():
"""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_MULTI_WWN
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 HBSDRESTFCDriverTest(test.TestCase):
"""Unit test class for HBSD REST 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(HBSDRESTFCDriverTest, self).setUp()
_set_required(rest_required_opts, True)
_set_required(common_required_opts, True)
self.configuration = mock.Mock(conf.Configuration)
self.ctxt = cinder_context.get_admin_context()
self._setup_config()
self._setup_driver()
def _setup_config(self):
"""Set configuration parameter values."""
self.configuration.config_group = "REST"
self.configuration.volume_backend_name = "RESTFC"
self.configuration.volume_driver = (
"cinder.volume.drivers.hitachi.hbsd_fc.HBSDFCDriver")
self.configuration.reserved_percentage = "0"
self.configuration.use_multipath_for_image_xfer = False
self.configuration.enforce_multipath_for_image_xfer = False
self.configuration.max_over_subscription_ratio = 500.0
self.configuration.driver_ssl_cert_verify = False
self.configuration.hitachi_storage_id = CONFIG_MAP['serial']
self.configuration.hitachi_pools = ["30"]
self.configuration.hitachi_snap_pool = None
self.configuration.hitachi_ldev_range = "0-1"
self.configuration.hitachi_target_ports = [CONFIG_MAP['port_id']]
self.configuration.hitachi_compute_target_ports = [
CONFIG_MAP['port_id']]
self.configuration.hitachi_group_create = True
self.configuration.hitachi_group_delete = True
self.configuration.hitachi_copy_speed = 3
self.configuration.hitachi_copy_check_interval = 3
self.configuration.hitachi_async_copy_check_interval = 10
self.configuration.hitachi_port_scheduler = False
self.configuration.hitachi_group_name_format = None
self.configuration.san_login = CONFIG_MAP['user_id']
self.configuration.san_password = CONFIG_MAP['user_pass']
self.configuration.san_ip = CONFIG_MAP[
'rest_server_ip_addr']
self.configuration.san_api_port = CONFIG_MAP[
'rest_server_ip_port']
self.configuration.hitachi_rest_disable_io_wait = True
self.configuration.hitachi_rest_tcp_keepalive = True
self.configuration.hitachi_discard_zero_page = True
self.configuration.hitachi_lun_timeout = hbsd_rest._LUN_TIMEOUT
self.configuration.hitachi_lun_retry_interval = (
hbsd_rest._LUN_RETRY_INTERVAL)
self.configuration.hitachi_restore_timeout = hbsd_rest._RESTORE_TIMEOUT
self.configuration.hitachi_state_transition_timeout = (
hbsd_rest._STATE_TRANSITION_TIMEOUT)
self.configuration.hitachi_lock_timeout = hbsd_rest_api._LOCK_TIMEOUT
self.configuration.hitachi_rest_timeout = hbsd_rest_api._REST_TIMEOUT
self.configuration.hitachi_extend_timeout = (
hbsd_rest_api._EXTEND_TIMEOUT)
self.configuration.hitachi_exec_retry_interval = (
hbsd_rest_api._EXEC_RETRY_INTERVAL)
self.configuration.hitachi_rest_connect_timeout = (
hbsd_rest_api._DEFAULT_CONNECT_TIMEOUT)
self.configuration.hitachi_rest_job_api_response_timeout = (
hbsd_rest_api._JOB_API_RESPONSE_TIMEOUT)
self.configuration.hitachi_rest_get_api_response_timeout = (
hbsd_rest_api._GET_API_RESPONSE_TIMEOUT)
self.configuration.hitachi_rest_server_busy_timeout = (
hbsd_rest_api._REST_SERVER_BUSY_TIMEOUT)
self.configuration.hitachi_rest_keep_session_loop_interval = (
hbsd_rest_api._KEEP_SESSION_LOOP_INTERVAL)
self.configuration.hitachi_rest_another_ldev_mapped_retry_timeout = (
hbsd_rest_api._ANOTHER_LDEV_MAPPED_RETRY_TIMEOUT)
self.configuration.hitachi_rest_tcp_keepidle = (
hbsd_rest_api._TCP_KEEPIDLE)
self.configuration.hitachi_rest_tcp_keepintvl = (
hbsd_rest_api._TCP_KEEPINTVL)
self.configuration.hitachi_rest_tcp_keepcnt = (
hbsd_rest_api._TCP_KEEPCNT)
self.configuration.hitachi_host_mode_options = []
self.configuration.hitachi_zoning_request = False
self.configuration.san_thin_provision = True
self.configuration.san_private_key = ''
self.configuration.san_clustername = ''
self.configuration.san_ssh_port = '22'
self.configuration.san_is_local = False
self.configuration.ssh_conn_timeout = '30'
self.configuration.ssh_min_pool_conn = '1'
self.configuration.ssh_max_pool_conn = '5'
self.configuration.use_chap_auth = True
self.configuration.chap_username = CONFIG_MAP['auth_user']
self.configuration.chap_password = CONFIG_MAP['auth_password']
self.configuration.hitachi_replication_number = 0
self.configuration.hitachi_pair_target_number = 0
self.configuration.hitachi_rest_pair_target_ports = []
self.configuration.hitachi_quorum_disk_id = ''
self.configuration.hitachi_mirror_copy_speed = ''
self.configuration.hitachi_mirror_storage_id = ''
self.configuration.hitachi_mirror_pool = ''
self.configuration.hitachi_mirror_ldev_range = ''
self.configuration.hitachi_mirror_target_ports = ''
self.configuration.hitachi_mirror_rest_user = ''
self.configuration.hitachi_mirror_rest_password = ''
self.configuration.hitachi_mirror_rest_api_ip = ''
self.configuration.hitachi_set_mirror_reserve_attribute = ''
self.configuration.hitachi_path_group_id = ''
self.configuration.safe_get = self._fake_safe_get
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)
request.side_effect = [FakeResponse(200, POST_SESSIONS_RESULT),
FakeResponse(200, GET_PORTS_RESULT),
FakeResponse(200, GET_HOST_WWNS_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)]
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.client.keep_session_loop.stop()
def tearDown(self):
self.client = None
super(HBSDRESTFCDriverTest, 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()
request.side_effect = [FakeResponse(200, POST_SESSIONS_RESULT),
FakeResponse(200, GET_PORTS_RESULT),
FakeResponse(200, GET_HOST_WWNS_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)]
drv.do_setup(None)
self.assertEqual(
{CONFIG_MAP['port_id']: CONFIG_MAP['target_wwn']},
drv.common.storage_info['wwns'])
self.assertEqual(1, brick_get_connector_properties.call_count)
self.assertEqual(4, request.call_count)
# stop the Loopingcall within the do_setup treatment
self.driver.common.client.keep_session_loop.stop()
self.driver.common.client.keep_session_loop.wait()
@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_create_hg(self, brick_get_connector_properties, request):
"""Normal case: The host group not exists."""
drv = hbsd_fc.HBSDFCDriver(
configuration=self.configuration)
self._setup_config()
request.side_effect = [FakeResponse(200, POST_SESSIONS_RESULT),
FakeResponse(200, GET_PORTS_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)]
drv.do_setup(None)
self.assertEqual(
{CONFIG_MAP['port_id']: CONFIG_MAP['target_wwn']},
drv.common.storage_info['wwns'])
self.assertEqual(1, brick_get_connector_properties.call_count)
self.assertEqual(9, request.call_count)
# stop the Loopingcall within the do_setup treatment
self.driver.common.client.keep_session_loop.stop()
self.driver.common.client.keep_session_loop.wait()
@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_create_hg_format(
self, brick_get_connector_properties, request):
drv = hbsd_fc.HBSDFCDriver(configuration=self.configuration)
self._setup_config()
self.configuration.hitachi_group_name_format = (
'HBSD-{wwn}-{host}-_:.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
request.side_effect = [FakeResponse(200, POST_SESSIONS_RESULT),
FakeResponse(200, GET_PORTS_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)]
drv.do_setup(None)
self.assertEqual(
{CONFIG_MAP['port_id']: CONFIG_MAP['target_wwn']},
drv.common.storage_info['wwns'])
self.assertEqual(1, brick_get_connector_properties.call_count)
self.assertEqual(9, request.call_count)
# stop the Loopingcall within the do_setup treatment
self.driver.common.client.keep_session_loop.stop()
self.driver.common.client.keep_session_loop.wait()
@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_create_hg_format_error(
self, brick_get_connector_properties, request):
drv = hbsd_fc.HBSDFCDriver(configuration=self.configuration)
self._setup_config()
self.configuration.hitachi_group_name_format = '{host}-{wwn}'
request.side_effect = [FakeResponse(200, POST_SESSIONS_RESULT),
FakeResponse(200, GET_PORTS_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
self.assertRaises(exception.VolumeDriverException, drv.do_setup, None)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(
volume_utils, 'brick_get_connector_properties',
side_effect=_brick_get_connector_properties_multi_wwn)
def test_do_setup_create_hg_port_scheduler(
self, brick_get_connector_properties, request):
"""Normal case: The host group not exists with port scheduler."""
drv = hbsd_fc.HBSDFCDriver(
configuration=self.configuration)
self._setup_config()
self.configuration.hitachi_port_scheduler = True
self.configuration.hitachi_zoning_request = True
drv.common._lookup_service = FakeLookupServiceMultiWwn()
request.side_effect = [FakeResponse(200, POST_SESSIONS_RESULT),
FakeResponse(200, GET_PORTS_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)]
drv.do_setup(None)
self.assertEqual(
{CONFIG_MAP['port_id']: CONFIG_MAP['target_wwn']},
drv.common.storage_info['wwns'])
self.assertEqual(1, brick_get_connector_properties.call_count)
self.assertEqual(10, request.call_count)
# stop the Loopingcall within the do_setup treatment
self.driver.common.client.keep_session_loop.stop()
self.driver.common.client.keep_session_loop.wait()
@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_pool_name(self, brick_get_connector_properties, request):
"""Normal case: Specify a pool name instead of pool id"""
drv = hbsd_fc.HBSDFCDriver(
configuration=self.configuration)
self._setup_config()
tmp_pools = self.configuration.hitachi_pools
self.configuration.hitachi_pools = [CONFIG_MAP['pool_name']]
request.side_effect = [FakeResponse(200, POST_SESSIONS_RESULT),
FakeResponse(200, GET_POOLS_RESULT),
FakeResponse(200, GET_PORTS_RESULT),
FakeResponse(200, GET_HOST_WWNS_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT_PAIR)]
drv.do_setup(None)
self.assertEqual(
{CONFIG_MAP['port_id']: CONFIG_MAP['target_wwn']},
drv.common.storage_info['wwns'])
self.assertEqual(1, brick_get_connector_properties.call_count)
self.assertEqual(5, request.call_count)
self.configuration.hitachi_pools = tmp_pools
# stop the Loopingcall within the do_setup treatment
self.driver.common.client.keep_session_loop.stop()
self.driver.common.client.keep_session_loop.wait()
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_create_volume_deduplication_compression(
self, get_volume_type_extra_specs, request):
extra_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
get_volume_type_extra_specs.return_value = extra_specs
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(TEST_VOLUME[3])
args, kwargs = request.call_args_list[0]
body = kwargs['json']
self.assertEqual(body.get('dataReductionMode'),
'compression_deduplication')
self.assertEqual('1', ret['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(2, request.call_count)
@reduce_retrying_time
@mock.patch.object(requests.Session, "request")
def test_create_volume_timeout(self, request):
self.driver.common.conf.hitachi_rest_timeout = 0
self.driver.common.conf.hitachi_exec_retry_interval = 0
request.return_value = FakeResponse(
500, ERROR_RESULT,
headers={'Content-Type': 'json'})
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
self.assertRaises(exception.VolumeDriverException,
self.driver.create_volume,
fake_volume.fake_volume_obj(self.ctxt))
self.assertGreater(request.call_count, 1)
@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(202, COMPLETED_SUCCEEDED_RESULT)]
self.driver.delete_volume(TEST_VOLUME[0])
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_delete_volume_wait_copy_pair_deleting(self, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(200, GET_SNAPSHOTS_RESULT_BUSY),
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(7, request.call_count)
@reduce_retrying_time
@mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall',
new=test_utils.ZeroIntervalLoopingCall)
@mock.patch.object(requests.Session, "request")
def test_delete_volume_request_failed(self, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(200, GET_SNAPSHOTS_RESULT_BUSY),
FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(200, GET_LDEV_RESULT_PAIR)]
self.assertRaises(exception.VolumeDriverException,
self.driver.delete_volume,
TEST_VOLUME[0])
self.assertGreater(request.call_count, 2)
@mock.patch.object(requests.Session, "request")
def test_delete_volume_volume_is_busy(self, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR)]
self.assertRaises(exception.VolumeIsBusy,
self.driver.delete_volume,
TEST_VOLUME[0])
self.assertEqual(2, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_extend_volume(self, request):
request.side_effect = [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(3, request.call_count)
@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.assertEqual(self.configuration.volume_backend_name,
stats["pools"][0]['pool_name'])
self.assertEqual(self.configuration.reserved_percentage,
stats["pools"][0]['reserved_percentage'])
self.assertTrue(stats["pools"][0]['thin_provisioning_support'])
self.assertFalse(stats["pools"][0]['thick_provisioning_support'])
self.assertTrue(stats["pools"][0]['multiattach'])
self.assertTrue(stats["pools"][0]['consistencygroup_support'])
self.assertTrue(stats["pools"][0]['consistent_group_snapshot_enabled'])
self.assertEqual(self.configuration.max_over_subscription_ratio,
stats["pools"][0]['max_over_subscription_ratio'])
self.assertEqual(
GET_POOL_RESULT['totalPoolCapacity'] // units.Ki,
stats["pools"][0]['total_capacity_gb'])
self.assertEqual(
GET_POOL_RESULT['availableVolumeCapacity'] // units.Ki,
stats["pools"][0]['free_capacity_gb'])
self.assertEqual(
GET_POOL_RESULT['totalLocatedCapacity'] // units.Ki,
stats["pools"][0]['provisioned_capacity_gb'])
self.assertEqual('up', stats["pools"][0]['backend_state'])
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(driver.FibreChannelDriver, "get_goodness_function")
@mock.patch.object(driver.FibreChannelDriver, "get_filter_function")
@mock.patch.object(hbsd_rest.HBSDREST, "get_pool_info")
def test_get_volume_stats_error(
self, get_pool_info, get_filter_function, get_goodness_function):
get_pool_info.side_effect = exception.VolumeDriverException(data='')
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.assertEqual(self.configuration.volume_backend_name,
stats["pools"][0]['pool_name'])
self.assertEqual(self.configuration.reserved_percentage,
stats["pools"][0]['reserved_percentage'])
self.assertTrue(stats["pools"][0]['thin_provisioning_support'])
self.assertFalse(stats["pools"][0]['thick_provisioning_support'])
self.assertTrue(stats["pools"][0]['multiattach'])
self.assertTrue(stats["pools"][0]['consistencygroup_support'])
self.assertTrue(stats["pools"][0]['consistent_group_snapshot_enabled'])
self.assertEqual(self.configuration.max_over_subscription_ratio,
stats["pools"][0]['max_over_subscription_ratio'])
self.assertEqual(0, stats["pools"][0]['total_capacity_gb'])
self.assertEqual(0, stats["pools"][0]['free_capacity_gb'])
self.assertEqual(0, stats["pools"][0]['provisioned_capacity_gb'])
self.assertEqual('down', stats["pools"][0]['backend_state'])
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)
def test_create_snapshot(
self, volume_get, get_volume_type_extra_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
get_volume_type_extra_specs.return_value = {}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(4, 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)
def test_create_snapshot_dedup_false(
self, volume_get, get_volume_type_extra_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
get_volume_type_extra_specs.return_value = {'hbsd:capacity_saving':
'disable'}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(4, 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")
def test_delete_snapshot_no_pair(self, request):
"""Normal case: Delete a snapshot without pair."""
request.side_effect = [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(4, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_create_cloned_volume(
self, get_volume_type_extra_specs, request):
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)]
get_volume_type_extra_specs.return_value = {}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
vol = self.driver.create_cloned_volume(TEST_VOLUME[0], TEST_VOLUME[1])
self.assertEqual('1', vol['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_create_volume_from_snapshot(
self, get_volume_type_extra_specs, request):
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._stats = {}
get_volume_type_extra_specs.return_value = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
vol = self.driver.create_volume_from_snapshot(
TEST_VOLUME[0], TEST_SNAPSHOT[0])
self.assertEqual('1', vol['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(5, request.call_count)
@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.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
extra_specs = {"hbsd:target_ports": "CL1-A"}
get_volume_type_extra_specs.return_value = extra_specs
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, "add_fc_zone")
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection_already_mapped(
self, get_volume_type_extra_specs, request, add_fc_zone):
"""Normal case: ldev have already mapped."""
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
extra_specs = {"hbsd:target_ports": "CL1-A"}
get_volume_type_extra_specs.return_value = extra_specs
request.side_effect = [
FakeResponse(200, GET_HOST_WWNS_RESULT),
FakeResponse(202, COMPLETED_FAILED_RESULT_LU_DEFINED),
FakeResponse(200, GET_LUNS_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(3, request.call_count)
self.assertEqual(1, add_fc_zone.call_count)
@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_shared_target(
self, get_volume_type_extra_specs, request, add_fc_zone):
"""Normal case: A target shared with other systems."""
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
extra_specs = {"hbsd:target_ports": "CL1-A"}
get_volume_type_extra_specs.return_value = extra_specs
request.side_effect = [FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT),
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(5, request.call_count)
self.assertEqual(1, add_fc_zone.call_count)
@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_create_target_to_storage_return(
self, get_volume_type_extra_specs, request, add_fc_zone):
self.configuration.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
extra_specs = {"hbsd:target_ports": "CL1-A"}
get_volume_type_extra_specs.return_value = extra_specs
request.side_effect = [
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(400, GET_HOST_GROUPS_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT_TEST),
FakeResponse(200, GET_HOST_GROUPS_RESULT_TEST),
]
self.assertRaises(exception.VolumeDriverException,
self.driver.initialize_connection,
TEST_VOLUME[1],
DEFAULT_CONNECTOR)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(10, request.call_count)
self.assertEqual(0, 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.driver.common.conf.hitachi_zoning_request = True
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, "remove_fc_zone")
@mock.patch.object(requests.Session, "request")
def test_terminate_connection_not_connector(self, request, remove_fc_zone):
"""Normal case: Connector is None."""
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_MAPPED),
FakeResponse(200, GET_HOST_GROUP_RESULT),
FakeResponse(200, GET_HOST_WWNS_RESULT),
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], None)
self.assertEqual(8, request.call_count)
self.assertEqual(1, remove_fc_zone.call_count)
@mock.patch.object(fczm_utils, "remove_fc_zone")
@mock.patch.object(requests.Session, "request")
def test_terminate_connection_not_lun(self, request, remove_fc_zone):
"""Normal case: Lun already not exist."""
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
request.side_effect = [FakeResponse(200, GET_HOST_WWNS_RESULT),
FakeResponse(200, GET_LDEV_RESULT)]
self.driver.terminate_connection(TEST_VOLUME[2], DEFAULT_CONNECTOR)
self.assertEqual(2, 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.driver.common.conf.hitachi_zoning_request = True
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.driver.common.conf.hitachi_zoning_request = True
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")
def test_manage_existing(self, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_name(self, request):
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref_name)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_get_size(self, request):
request.return_value = FakeResponse(200, GET_LDEV_RESULT)
self.driver.manage_existing_get_size(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual(1, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_get_size_name(self, request):
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT)]
self.driver.manage_existing_get_size(
TEST_VOLUME[0], self.test_existing_ref_name)
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)]
self.driver.unmanage(TEST_VOLUME[0])
self.assertEqual(2, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_unmanage_volume_is_busy(self, request):
request.side_effect = [
FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
]
self.assertRaises(exception.VolumeIsBusy,
self.driver.unmanage,
TEST_VOLUME[1])
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_unmanage_volume_is_busy_raise_ex(self, request):
request.side_effect = [
FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(200, GET_LDEV_RESULT_PAIR),
FakeResponse(400, GET_SNAPSHOTS_RESULT_BUSY)
]
self.assertRaises(exception.VolumeDriverException,
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.return_value = FakeResponse(202, 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)
self.assertEqual(1, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_update_migrated_volume(self, request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
self.assertRaises(
NotImplementedError,
self.driver.update_migrated_volume,
self.ctxt,
TEST_VOLUME[0],
TEST_VOLUME[1],
"available")
self.assertEqual(1, request.call_count)
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")
def test_retype(self, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
host = {
'capabilities': {
'location_info': {
'pool_id': 30,
},
},
}
new_type = {'extra_specs': {
'hbsd:capacity_saving': 'deduplication_compression'}}
old_specs = {'hbsd:capacity_saving': 'disable'}
new_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
old_type_ref = volume_types.create(self.ctxt, 'old', old_specs)
new_type_ref = volume_types.create(self.ctxt, 'new', new_specs)
diff = volume_types.volume_types_diff(self.ctxt, old_type_ref['id'],
new_type_ref['id'])[0]
ret = self.driver.retype(
self.ctxt, TEST_VOLUME[0], new_type, diff, host)
self.assertEqual(3, request.call_count)
self.assertTrue(ret)
@mock.patch.object(requests.Session, "request")
def test_migrate_volume(self, request):
request.return_value = FakeResponse(200, GET_LDEV_RESULT)
host = {
'capabilities': {
'location_info': {
'storage_id': CONFIG_MAP['serial'],
'pool_id': 30,
},
},
}
ret = self.driver.migrate_volume(self.ctxt, TEST_VOLUME[0], host)
self.assertEqual(2, 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')
def test_migrate_volume_diff_pool(
self, get_volume_type_extra_specs, request):
get_volume_type_extra_specs.return_value = {}
request.side_effect = [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, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
host = {
'capabilities': {
'location_info': {
'storage_id': CONFIG_MAP['serial'],
'pool_id': 40,
},
},
}
ret = self.driver.migrate_volume(self.ctxt, TEST_VOLUME[0], host)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(15, request.call_count)
actual = (True, {'provider_location': '1'})
self.assertTupleEqual(actual, ret)
def test_backup_use_temp_snapshot(self):
self.assertTrue(self.driver.backup_use_temp_snapshot())
@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_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(5, request.call_count)
def test_session___call__(self):
session = self.driver.common.client.Session('id', 'token')
req = models.Response()
ret = session.__call__(req)
self.assertEqual('Session token', ret.headers['Authorization'])
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_extra_specs')
def test_create_group_from_src_volume(
self, get_volume_type_extra_specs, request):
get_volume_type_extra_specs.return_value = {}
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._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
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.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(5, request.call_count)
actual = (
None, [{'id': TEST_VOLUME[1]['id'], 'provider_location': '1'}])
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_create_group_from_src_snapshot(
self, get_volume_type_extra_specs, request):
get_volume_type_extra_specs.return_value = {}
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._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
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.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(5, request.call_count)
actual = (
None, [{'id': TEST_VOLUME[0]['id'], 'provider_location': '1'}])
self.assertTupleEqual(actual, ret)
def test_create_group_from_src_volume_error(self):
self.assertRaises(
exception.VolumeDriverException, self.driver.create_group_from_src,
self.ctxt, TEST_GROUP[1], [TEST_VOLUME[1]],
source_group=TEST_GROUP[0], source_vols=[TEST_VOLUME[3]]
)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
def test_update_group(self, is_group_a_cg_snapshot_type):
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)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
def test_update_group_error(self, is_group_a_cg_snapshot_type):
is_group_a_cg_snapshot_type.return_value = True
self.assertRaises(
exception.VolumeDriverException, self.driver.update_group,
self.ctxt, TEST_GROUP[0], add_volumes=[TEST_VOLUME[3]],
remove_volumes=[TEST_VOLUME[0]]
)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
def test_create_group_snapshot_non_cg(
self, is_group_a_cg_snapshot_type, get_volume_type_extra_specs,
volume_get, request):
is_group_a_cg_snapshot_type.return_value = False
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_group_snapshot(
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(4, request.call_count)
actual = (
{'status': 'available'},
[{'id': TEST_SNAPSHOT[0]['id'],
'provider_location': '1',
'status': 'available'}]
)
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
def test_create_group_snapshot_cg(
self, is_group_a_cg_snapshot_type, get_volume_type_extra_specs,
volume_get, request):
is_group_a_cg_snapshot_type.return_value = True
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_group_snapshot(
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(5, request.call_count)
actual = (
None,
[{'id': TEST_SNAPSHOT[0]['id'],
'provider_location': '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(hbsd_fc.HBSDFCDriver, "_get_oslo_driver_opts")
def test_get_driver_options(self, _get_oslo_driver_opts):
_get_oslo_driver_opts.return_value = []
ret = self.driver.get_driver_options()
actual = (hbsd_common.COMMON_VOLUME_OPTS +
hbsd_common.COMMON_PORT_OPTS +
hbsd_common.COMMON_PAIR_OPTS +
hbsd_common.COMMON_NAME_OPTS +
hbsd_rest.REST_VOLUME_OPTS +
hbsd_rest.REST_PAIR_OPTS +
hbsd_rest_fc.FC_VOLUME_OPTS +
hbsd_replication._REP_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)
self.assertEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_is_modifiable_dr_value_new_dr_mode_disabled(self, request):
request.side_effect = [
FakeResponse(200, GET_LDEV_RESULT_PAIR_STATUS_TEST),
FakeResponse(200, GET_LDEV_RESULT_PAIR_STATUS_TEST),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
]
host = {
'capabilities': {
'location_info': {
'pool_id': 30,
},
},
}
new_type = {'extra_specs': {'hbsd:capacity_saving': 'disable'}}
old_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
new_specs = {'hbsd:capacity_saving': 'disable'}
old_type_ref = volume_types.create(self.ctxt, 'old', old_specs)
new_type_ref = volume_types.create(self.ctxt, 'new', new_specs)
diff = volume_types.volume_types_diff(self.ctxt, old_type_ref['id'],
new_type_ref['id'])[0]
ret = self.driver.retype(
self.ctxt, TEST_VOLUME[0], new_type, diff, host)
self.assertEqual(3, request.call_count)
self.assertTrue(ret)