cinder/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py

11137 lines
465 KiB
Python

# (c) Copyright 2013-2015 Hewlett Packard Enterprise Development LP
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Unit tests for OpenStack Cinder volume drivers."""
import ast
import copy
from unittest import mock
import ddt
from oslo_config import cfg
from oslo_utils import units
from oslo_utils import uuidutils
from cinder import context
from cinder import exception
from cinder.objects import fields
from cinder import test
from cinder.tests.unit import fake_volume
from cinder.tests.unit.volume.drivers.hpe \
import fake_hpe_3par_client as hpe3parclient
from cinder.volume.drivers.hpe import hpe_3par_base as hpedriverbase
from cinder.volume.drivers.hpe import hpe_3par_common as hpecommon
from cinder.volume.drivers.hpe import hpe_3par_fc as hpefcdriver
from cinder.volume.drivers.hpe import hpe_3par_iscsi as hpedriver
from cinder.volume import qos_specs
from cinder.volume import volume_types
from cinder.volume import volume_utils
hpeexceptions = hpe3parclient.hpeexceptions
CONF = cfg.CONF
HPE3PAR_CPG = 'OpenStackCPG'
HPE3PAR_CPG2 = 'fakepool'
HPE3PAR_CPG_QOS = 'qospool'
HPE3PAR_CPG_SNAP = 'OpenStackCPGSnap'
HPE3PAR_USER_NAME = 'testUser'
HPE3PAR_USER_PASS = 'testPassword'
HPE3PAR_SAN_IP = '2.2.2.2'
HPE3PAR_SAN_SSH_PORT = 999
HPE3PAR_SAN_SSH_CON_TIMEOUT = 44
HPE3PAR_SAN_SSH_PRIVATE = 'foobar'
GOODNESS_FUNCTION = \
"stats.capacity_utilization < 0.6? 100:25"
FILTER_FUNCTION = \
"stats.total_volumes < 400 && stats.capacity_utilization < 0.8"
CHAP_USER_KEY = "HPQ-cinder-CHAP-name"
CHAP_PASS_KEY = "HPQ-cinder-CHAP-secret"
FLASH_CACHE_ENABLED = 1
FLASH_CACHE_DISABLED = 2
# Input/output (total read/write) operations per second.
THROUGHPUT = 'throughput'
# Data processed (total read/write) per unit time: kilobytes per second.
BANDWIDTH = 'bandwidth'
# Response time (total read/write): microseconds.
LATENCY = 'latency'
# IO size (total read/write): kilobytes.
IO_SIZE = 'io_size'
# Queue length for processing IO requests
QUEUE_LENGTH = 'queue_length'
# Average busy percentage
AVG_BUSY_PERC = 'avg_busy_perc'
# replication constants
HPE3PAR_CPG_REMOTE = 'DestOpenStackCPG'
HPE3PAR_CPG2_REMOTE = 'destfakepool'
HPE3PAR_CPG_MAP = 'OpenStackCPG:DestOpenStackCPG fakepool:destfakepool'
SYNC_MODE = 1
PERIODIC_MODE = 2
SYNC_PERIOD = 900
# EXISTENT_PATH error code returned from hpe3parclient
EXISTENT_PATH = 73
class Comment(object):
def __init__(self, expected):
self.expected = expected
def __eq__(self, actual):
return (dict(ast.literal_eval(actual)) == self.expected)
def __ne__(self, other):
return not self.__eq__(other)
class HPE3PARBaseDriver(test.TestCase):
VOLUME_ID = 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'
SRC_CG_VOLUME_ID = 'bd21d11b-c765-4c68-896c-6b07f63cfcb6'
CLONE_ID = 'd03338a9-9115-48a3-8dfc-000000000000'
VOLUME_TYPE_ID_REPLICATED = 'be9181f1-4040-46f2-8298-e7532f2bf9db'
VOLUME_TYPE_ID_DEDUP = 'd03338a9-9115-48a3-8dfc-11111111111'
VOLUME_TYPE_ID_TIRAMISU = 'd03338a9-9115-48a3-8dfc-44444444444'
VOL_TYPE_ID_DEDUP_COMPRESS = 'd03338a9-9115-48a3-8dfc-33333333333'
VOLUME_TYPE_ID_FLASH_CACHE = 'd03338a9-9115-48a3-8dfc-22222222222'
VOLUME_NAME = 'volume-' + VOLUME_ID
SRC_CG_VOLUME_NAME = 'volume-' + SRC_CG_VOLUME_ID
VOLUME_NAME_3PAR = 'osv-0DM4qZEVSKON-DXN-NwVpw'
SNAPSHOT_ID = '2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
SNAPSHOT_NAME = 'snapshot-2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
VOLUME_3PAR_NAME = 'osv-0DM4qZEVSKON-DXN-NwVpw'
VOLUME_NAME_ID_3PAR_NAME = 'osv-L4I73ONuTci9Fd4ceij-MQ'
SNAPSHOT_3PAR_NAME = 'oss-L4I73ONuTci9Fd4ceij-MQ'
RCG_3PAR_NAME = 'rcg-0DM4qZEVSKON-DXN-N'
RCG_3PAR_GROUP_NAME = 'rcg-YET.38iJR1KQDyA50k'
GROUP_ID = '6044fedf-c889-4752-900f-2039d247a5df'
CONSIS_GROUP_NAME = 'vvs-YET.38iJR1KQDyA50kel3w'
SRC_CONSIS_GROUP_ID = '7d7dfa02-ac6e-48cb-96af-8a0cd3008d47'
SRC_CONSIS_GROUP_NAME = 'vvs-fX36AqxuSMuWr4oM0wCNRw'
CGSNAPSHOT_ID = 'e91c5ed5-daee-4e84-8724-1c9e31e7a1f2'
CGSNAPSHOT_BASE_NAME = 'oss-6Rxe1druToSHJByeMeeh8g'
CLIENT_ID = "12345"
REPLICATION_CLIENT_ID = "54321"
REPLICATION_BACKEND_ID = 'target'
# fake host on the 3par
FAKE_HOST = 'fakehost'
FAKE_CINDER_HOST = 'fakehost@foo#' + HPE3PAR_CPG
USER_ID = '2689d9a913974c008b1d859013f23607'
PROJECT_ID = 'fac88235b9d64685a3530f73e490348f'
VOLUME_ID_SNAP = '761fc5e5-5191-4ec7-aeba-33e36de44156'
FAKE_DESC = 'test description name'
FAKE_FC_PORTS = [{'portPos': {'node': 7, 'slot': 1, 'cardPort': 1},
'type': 1,
'portWWN': '0987654321234',
'protocol': 1,
'mode': 2,
'linkState': 4},
{'portPos': {'node': 6, 'slot': 1, 'cardPort': 1},
'type': 1,
'portWWN': '123456789000987',
'protocol': 1,
'mode': 2,
'linkState': 4}]
QOS = {'qos:maxIOPS': '1000', 'qos:maxBWS': '50',
'qos:minIOPS': '100', 'qos:minBWS': '25',
'qos:latency': '25', 'qos:priority': 'low'}
QOS_SPECS = {'maxIOPS': '1000', 'maxBWS': '50',
'minIOPS': '100', 'minBWS': '25',
'latency': '25', 'priority': 'low'}
VVS_NAME = "myvvs"
FAKE_ISCSI_PORT = {'portPos': {'node': 8, 'slot': 1, 'cardPort': 1},
'protocol': 2,
'mode': 2,
'IPAddr': '1.1.1.2',
'iSCSIName': ('iqn.2000-05.com.3pardata:'
'21810002ac00383d'),
'linkState': 4}
volume_snapshot = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None}
volume = fake_volume.fake_volume_obj(
context.get_admin_context(),
name=VOLUME_NAME,
id=VOLUME_ID,
display_name='Foo Volume',
size=2,
host=FAKE_CINDER_HOST,
volume_type=None,
volume_type_id=None,
multiattach=False)
volume_name_id = fake_volume.fake_volume_obj(
context.get_admin_context(),
id=VOLUME_ID,
_name_id='2f823bdc-e36e-4dc8-bd15-de1c7a28ff31',
size=2,
host=FAKE_CINDER_HOST,
volume_type=None,
volume_type_id=None)
volume_src_cg = {'name': SRC_CG_VOLUME_NAME,
'id': SRC_CG_VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None}
volume_replicated = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'replication_status': 'disabled',
'provider_location': CLIENT_ID,
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': 'replicated',
'volume_type_id': VOLUME_TYPE_ID_REPLICATED}
volume_tiramisu = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'replication_status': 'disabled',
'provider_location': CLIENT_ID,
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': 'group_replication',
'volume_type_id': VOLUME_TYPE_ID_TIRAMISU}
replication_targets = [{'backend_id': REPLICATION_BACKEND_ID,
'cpg_map': HPE3PAR_CPG_MAP,
'hpe3par_api_url': 'https://1.1.1.1/api/v1',
'hpe3par_username': HPE3PAR_USER_NAME,
'hpe3par_password': HPE3PAR_USER_PASS,
'san_ip': HPE3PAR_SAN_IP,
'san_login': HPE3PAR_USER_NAME,
'san_password': HPE3PAR_USER_PASS,
'san_ssh_port': HPE3PAR_SAN_SSH_PORT,
'ssh_conn_timeout': HPE3PAR_SAN_SSH_CON_TIMEOUT,
'san_private_key': HPE3PAR_SAN_SSH_PRIVATE}]
list_rep_targets = [{'backend_id': 'target'}]
volume_encrypted = fake_volume.fake_volume_obj(
context.get_admin_context(),
name=VOLUME_NAME,
id=VOLUME_ID,
display_name='Foo Volume',
size=2,
host=FAKE_CINDER_HOST,
volume_type=None,
volume_type_id=None,
encryption_key_id=uuidutils.generate_uuid())
volume_dedup_compression = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 16,
'host': FAKE_CINDER_HOST,
'volume_type': 'dedup_compression',
'volume_type_id': VOL_TYPE_ID_DEDUP_COMPRESS}
volume_dedup = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': 'dedup',
'volume_type_id': VOLUME_TYPE_ID_DEDUP}
volume_pool = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': volume_utils.append_host(FAKE_HOST, HPE3PAR_CPG2),
'volume_type': None,
'volume_type_id': None}
volume_qos = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': 'gold'}
volume_flash_cache = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': VOLUME_TYPE_ID_FLASH_CACHE}
volume_hos = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': 'hos'}
snapshot_volume = {'name': VOLUME_NAME,
'id': VOLUME_ID_SNAP,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None}
snapshot = {'name': SNAPSHOT_NAME,
'id': SNAPSHOT_ID,
'user_id': USER_ID,
'project_id': PROJECT_ID,
'volume_id': VOLUME_ID_SNAP,
'volume_name': VOLUME_NAME,
'status': fields.SnapshotStatus.CREATING,
'progress': '0%',
'volume_size': 2,
'display_name': 'fakesnap',
'display_description': FAKE_DESC,
'volume': snapshot_volume}
snapshot_name_id = {'id': SNAPSHOT_ID,
'volume_id': volume_name_id.id,
'volume_size': 2,
'volume': volume_name_id,
'display_name': 'display-name',
'display_description': 'description',
'volume_name': 'name'}
wwn = ["123456789012345", "123456789054321"]
connector = {'ip': '10.0.0.2',
'initiator': 'iqn.1993-08.org.debian:01:222',
'wwpns': [wwn[0], wwn[1]],
'wwnns': ["223456789012345", "223456789054321"],
'host': FAKE_HOST,
'multipath': False}
connector_multipath_enabled = {'ip': '10.0.0.2',
'initiator': ('iqn.1993-08.org'
'.debian:01:222'),
'wwpns': [wwn[0], wwn[1]],
'wwnns': ["223456789012345",
"223456789054321"],
'host': FAKE_HOST,
'multipath': True}
volume_type = {'name': 'gold',
'deleted': False,
'updated_at': None,
'extra_specs': {'cpg': HPE3PAR_CPG2,
'qos:maxIOPS': '1000',
'qos:maxBWS': '50',
'qos:minIOPS': '100',
'qos:minBWS': '25',
'qos:latency': '25',
'qos:priority': 'low'},
'deleted_at': None,
'id': 'gold'}
volume_type_replicated = {'name': 'replicated',
'deleted': False,
'updated_at': None,
'extra_specs':
{'replication_enabled': '<is> True'},
'deleted_at': None,
'id': VOLUME_TYPE_ID_REPLICATED}
volume_type_dedup_compression = {'name': 'dedup',
'deleted': False,
'updated_at': None,
'extra_specs': {'cpg': HPE3PAR_CPG2,
'provisioning': 'dedup',
'compression': 'true'},
'deleted_at': None,
'id': VOL_TYPE_ID_DEDUP_COMPRESS}
volume_type_dedup = {'name': 'dedup',
'deleted': False,
'updated_at': None,
'extra_specs': {'cpg': HPE3PAR_CPG2,
'provisioning': 'dedup'},
'deleted_at': None,
'id': VOLUME_TYPE_ID_DEDUP}
volume_type_tiramisu = {'name': 'dedup',
'deleted': False,
'updated_at': None,
'extra_specs': {'cpg': HPE3PAR_CPG2,
'hpe3par:group_replication':
'<is> True',
'replication_enabled': '<is> True',
'replication:mode': 'sync'},
'deleted_at': None,
'id': VOLUME_TYPE_ID_TIRAMISU}
volume_type_flash_cache = {'name': 'flash-cache-on',
'deleted': False,
'updated_at': None,
'extra_specs': {'cpg': HPE3PAR_CPG2,
'hpe3par:flash_cache': 'true'},
'deleted_at': None,
'id': VOLUME_TYPE_ID_FLASH_CACHE}
volume_type_hos = {'name': 'hos',
'deleted': False,
'updated_at': None,
'extra_specs': {'convert_to_base': False},
'deleted_at': None,
'id': 'hos'}
flash_cache_3par_keys = {'flash_cache': 'true'}
cpgs = [
{'SAGrowth': {'LDLayout': {'diskPatterns': [{'diskType': 2}]},
'incrementMiB': 8192},
'SAUsage': {'rawTotalMiB': 24576,
'rawUsedMiB': 768,
'totalMiB': 8192,
'usedMiB': 256},
'SDGrowth': {'LDLayout': {'RAIDType': 4,
'diskPatterns': [{'diskType': 2}]},
'incrementMiB': 32768},
'SDUsage': {'rawTotalMiB': 49152,
'rawUsedMiB': 1023,
'totalMiB': 36864,
'usedMiB': 1024 * 1},
'UsrUsage': {'rawTotalMiB': 57344,
'rawUsedMiB': 43349,
'totalMiB': 43008,
'usedMiB': 1024 * 20},
'additionalStates': [],
'degradedStates': [],
'failedStates': [],
'id': 5,
'name': HPE3PAR_CPG,
'numFPVVs': 2,
'numTPVVs': 0,
'numTDVVs': 1,
'state': 1,
'uuid': '29c214aa-62b9-41c8-b198-543f6cf24edf'}]
TASK_DONE = 1
TASK_ACTIVE = 2
STATUS_DONE = {'status': 1}
STATUS_ACTIVE = {'status': 2}
mock_client_conf = {
'PORT_MODE_TARGET': 2,
'PORT_STATE_READY': 4,
'PORT_PROTO_ISCSI': 2,
'PORT_PROTO_FC': 1,
'PORT_TYPE_HOST': 1,
'TASK_DONE': TASK_DONE,
'TASK_ACTIVE': TASK_ACTIVE,
'HOST_EDIT_ADD': 1,
'HOST_EDIT_REMOVE': 2,
'CHAP_INITIATOR': 1,
'CHAP_TARGET': 2,
'getPorts.return_value': {
'members': FAKE_FC_PORTS + [FAKE_ISCSI_PORT]
}
}
RETYPE_VVS_NAME = "yourvvs"
RETYPE_HOST = {
u'host': u'mark-stack1@3parfc',
u'capabilities': {
'QoS_support': True,
u'location_info': u'HPE3PARDriver:1234567:MARK_TEST_CPG',
u'timestamp': u'2014-06-04T19:03:32.485540',
u'allocated_capacity_gb': 0,
u'volume_backend_name': u'3parfc',
u'free_capacity_gb': u'infinite',
u'driver_version': u'3.0.0',
u'total_capacity_gb': u'infinite',
u'reserved_percentage': 0,
u'vendor_name': u'Hewlett Packard Enterprise',
u'storage_protocol': u'FC'
}
}
RETYPE_HOST_NOT3PAR = {
u'host': u'mark-stack1@3parfc',
u'capabilities': {
u'location_info': u'XXXDriverXXX:1610771:MARK_TEST_CPG',
}
}
RETYPE_QOS_SPECS = {'maxIOPS': '1000', 'maxBWS': '50',
'minIOPS': '100', 'minBWS': '25',
'latency': '25', 'priority': 'high'}
RETYPE_VOLUME_TYPE_ID = "FakeVolId"
RETYPE_VOLUME_TYPE_0 = {
'name': 'red',
'id': RETYPE_VOLUME_TYPE_ID,
'extra_specs': {
'cpg': HPE3PAR_CPG,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs': RETYPE_VVS_NAME,
'qos': RETYPE_QOS_SPECS,
'tpvv': True,
'tdvv': False,
'volume_type': volume_type
}
}
RETYPE_VOLUME_TYPE_1 = {
'name': 'white',
'id': RETYPE_VOLUME_TYPE_ID,
'extra_specs': {
'cpg': HPE3PAR_CPG,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs': VVS_NAME,
'qos': QOS,
'tpvv': True,
'tdvv': False,
'volume_type': volume_type
}
}
RETYPE_VOLUME_TYPE_2 = {
'name': 'blue',
'id': RETYPE_VOLUME_TYPE_ID,
'extra_specs': {
'cpg': HPE3PAR_CPG_QOS,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs': RETYPE_VVS_NAME,
'qos': RETYPE_QOS_SPECS,
'tpvv': True,
'tdvv': False,
'volume_type': volume_type
}
}
RETYPE_VOLUME_TYPE_3 = {
'name': 'purple',
'id': RETYPE_VOLUME_TYPE_ID,
'extra_specs': {
'cpg': HPE3PAR_CPG_QOS,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs': RETYPE_VVS_NAME,
'qos': RETYPE_QOS_SPECS,
'tpvv': False,
'tdvv': True,
'volume_type': volume_type
}
}
RETYPE_VOLUME_TYPE_BAD_PERSONA = {
'name': 'bad_persona',
'id': 'any_id',
'extra_specs': {
'hpe3par:persona': '99 - invalid'
}
}
RETYPE_VOLUME_TYPE_BAD_CPG = {
'name': 'bad_cpg',
'id': 'any_id',
'extra_specs': {
'cpg': 'bogus',
'snap_cpg': 'bogus',
'hpe3par:persona': '2 - Generic-ALUA'
}
}
MANAGE_VOLUME_INFO = {
'userCPG': 'testUserCpg0',
'snapCPG': 'testSnapCpg0',
'provisioningType': 1,
'comment': "{'display_name': 'Foo Volume'}"
}
MV_INFO_WITH_NO_SNAPCPG = {
'userCPG': 'testUserCpg0',
'provisioningType': 1,
'comment': "{'display_name': 'Foo Volume'}"
}
RETYPE_TEST_COMMENT = "{'retype_test': 'test comment'}"
RETYPE_VOLUME_INFO_0 = {
'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Retype Vol0',
'size': 1,
'host': RETYPE_HOST,
'userCPG': 'testUserCpg0',
'snapCPG': 'testSnapCpg0',
'provisioningType': 1,
'comment': RETYPE_TEST_COMMENT
}
RETYPE_TEST_COMMENT_1 = "{'retype_test': 'test comment 1'}"
RETYPE_VOLUME_INFO_1 = {
'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Retype Vol1',
'size': 1,
'host': RETYPE_HOST,
'userCPG': HPE3PAR_CPG,
'snapCPG': HPE3PAR_CPG_SNAP,
'provisioningType': 1,
'comment': RETYPE_TEST_COMMENT
}
RETYPE_TEST_COMMENT_2 = "{'retype_test': 'test comment 2'}"
RETYPE_VOLUME_INFO_2 = {
'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Retype Vol2',
'size': 1,
'host': RETYPE_HOST,
'userCPG': HPE3PAR_CPG,
'snapCPG': HPE3PAR_CPG_SNAP,
'provisioningType': 3,
'comment': RETYPE_TEST_COMMENT
}
# Test for when we don't get a snapCPG.
RETYPE_VOLUME_INFO_NO_SNAP = {
'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Retype Vol2',
'size': 1,
'host': RETYPE_HOST,
'userCPG': 'testUserCpg2',
'provisioningType': 1,
'comment': '{}'
}
RETYPE_CONF = {
'TASK_ACTIVE': TASK_ACTIVE,
'TASK_DONE': TASK_DONE,
'getTask.return_value': STATUS_DONE,
'getStorageSystemInfo.return_value': {'id': CLIENT_ID,
'serialNumber': '1234567'},
'getVolume.return_value': RETYPE_VOLUME_INFO_0,
'modifyVolume.return_value': ("anyResponse", {'taskid': 1})
}
# 3PAR retype currently doesn't use the diff. Existing code and fresh info
# from the array work better for the most part. Some use of the diff was
# intentionally removed to make _retype more usable for other use cases.
RETYPE_DIFF = None
wsapi_version_312 = {'major': 1,
'build': 30102422,
'minor': 3,
'revision': 1}
wsapi_version_for_compression = {'major': 1,
'build': 30301215,
'minor': 6,
'revision': 0}
wsapi_version_for_dedup = {'major': 1,
'build': 30201120,
'minor': 4,
'revision': 1}
wsapi_version_for_flash_cache = {'major': 1,
'build': 30201200,
'minor': 4,
'revision': 2}
wsapi_version_for_remote_copy = {'major': 1,
'build': 30202290,
'minor': 5,
'revision': 0}
# Use this to point to latest version of wsapi
wsapi_version_latest = wsapi_version_for_compression
standard_login = [
mock.call.login(HPE3PAR_USER_NAME, HPE3PAR_USER_PASS),
mock.call.setSSHOptions(
HPE3PAR_SAN_IP,
HPE3PAR_USER_NAME,
HPE3PAR_USER_PASS,
missing_key_policy='AutoAddPolicy',
privatekey=HPE3PAR_SAN_SSH_PRIVATE,
known_hosts_file=mock.ANY,
port=HPE3PAR_SAN_SSH_PORT,
conn_timeout=HPE3PAR_SAN_SSH_CON_TIMEOUT)]
get_id_login = [
mock.call.getWsApiVersion(),
mock.call.login(HPE3PAR_USER_NAME, HPE3PAR_USER_PASS),
mock.call.setSSHOptions(
HPE3PAR_SAN_IP,
HPE3PAR_USER_NAME,
HPE3PAR_USER_PASS,
missing_key_policy='AutoAddPolicy',
privatekey=HPE3PAR_SAN_SSH_PRIVATE,
known_hosts_file=mock.ANY,
port=HPE3PAR_SAN_SSH_PORT,
conn_timeout=HPE3PAR_SAN_SSH_CON_TIMEOUT),
mock.call.getStorageSystemInfo()]
standard_logout = [
mock.call.logout()]
@staticmethod
def fake_volume_object(vol_id='d03338a9-9115-48a3-8dfc-35cdfcdc15a7',
**kwargs):
values = dict(id=vol_id,
name='volume-%s' % vol_id,
display_name='Foo Volume',
size=2,
host='fakehost@foo#OpenStackCPG',
volume_type=None,
volume_type_id=None)
values.update(kwargs)
return fake_volume.fake_volume_obj(context.get_admin_context(),
**values)
class fake_group_object(object):
def __init__(self, grp_id='6044fedf-c889-4752-900f-2039d247a5df'):
self.id = grp_id
self.volume_type_ids = ['d03338a9-9115-48a3-8dfc-33333333333']
self.volume_types = ['d03338a9-9115-48a3-8dfc-33333333333']
self.name = 'cg_name'
self.group_snapshot_id = None
self.host = 'fakehost@foo#OpenStackCPG'
self.is_replicated = False
self.description = 'consistency group'
class fake_group_snapshot_object(object):
def __init__(self, cgsnap_id='e91c5ed5-daee-4e84-8724-1c9e31e7a1f2'):
self.id = cgsnap_id
self.group_id = '6044fedf-c889-4752-900f-2039d247a5df'
self.description = 'group_snapshot'
self.readOnly = False
def setup_configuration(self):
configuration = mock.MagicMock()
configuration.hpe3par_debug = False
configuration.hpe3par_username = HPE3PAR_USER_NAME
configuration.hpe3par_password = HPE3PAR_USER_PASS
configuration.hpe3par_api_url = 'https://1.1.1.1/api/v1'
configuration.hpe3par_cpg = [HPE3PAR_CPG, HPE3PAR_CPG2]
configuration.hpe3par_cpg_snap = HPE3PAR_CPG_SNAP
configuration.target_ip_address = '1.1.1.2'
configuration.target_port = '1234'
configuration.san_ip = HPE3PAR_SAN_IP
configuration.san_login = HPE3PAR_USER_NAME
configuration.san_password = HPE3PAR_USER_PASS
configuration.san_ssh_port = HPE3PAR_SAN_SSH_PORT
configuration.ssh_conn_timeout = HPE3PAR_SAN_SSH_CON_TIMEOUT
configuration.san_private_key = HPE3PAR_SAN_SSH_PRIVATE
configuration.hpe3par_snapshot_expiration = ""
configuration.hpe3par_snapshot_retention = ""
configuration.hpe3par_iscsi_ips = []
configuration.hpe3par_iscsi_chap_enabled = False
configuration.goodness_function = GOODNESS_FUNCTION
configuration.filter_function = FILTER_FUNCTION
configuration.image_volume_cache_enabled = False
configuration.replication_device = None
configuration.hpe3par_target_nsp = None
configuration.unique_fqdn_network = True
return configuration
@mock.patch(
'hpe3parclient.client.HPE3ParClient',
spec=True,
)
def setup_mock_client(self, _m_client, driver, conf=None, m_conf=None,
is_primera=False):
_m_client = _m_client.return_value
# Configure the base constants, defaults etc...
_m_client.configure_mock(**self.mock_client_conf)
_m_client.getWsApiVersion.return_value = self.wsapi_version_latest
_m_client.is_primera_array.return_value = is_primera
# If m_conf, drop those over the top of the base_conf.
if m_conf is not None:
_m_client.configure_mock(**m_conf)
if conf is None:
conf = self.setup_configuration()
self.driver = driver(configuration=conf)
self.driver.do_setup(None)
return _m_client
@mock.patch.object(volume_types, 'get_volume_type')
def migrate_volume_attached(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'volume_type_id': None,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'status': 'available',
'host': HPE3PARBaseDriver.FAKE_HOST,
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume_name_3par = common._encode_name(volume['id'])
osv_matcher = 'osv-' + volume_name_3par
loc_info = 'HPE3PARDriver:1234567:CPG-FC1'
protocol = "FC"
if self.properties['driver_volume_type'] == "iscsi":
protocol = "iSCSI"
host = {'host': 'stack@3parfc1',
'capabilities': {'location_info': loc_info,
'storage_protocol': protocol}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
new_comment = Comment({
"qos": {},
"retype_test": "test comment",
})
expected = [
mock.call.modifyVolume(osv_matcher,
{'comment': new_comment,
'snapCPG': 'OpenStackCPGSnap'}),
mock.call.modifyVolume(osv_matcher,
{'action': 6,
'userCPG': 'OpenStackCPG',
'conversionOperation': 1,
'tuneOperation': 1,
'compression': False}),
mock.call.getTask(1),
mock.call.logout()
]
mock_client.assert_has_calls(expected)
self.assertIsNotNone(result)
self.assertEqual((True, {'host': 'stack@3parfc1#OpenStackCPG'}),
result)
@ddt.ddt
class TestHPE3PARDriverBase(HPE3PARBaseDriver):
def setup_driver(self, config=None, mock_conf=None, wsapi_version=None):
self.ctxt = context.get_admin_context()
mock_client = self.setup_mock_client(
conf=config,
m_conf=mock_conf,
driver=hpedriverbase.HPE3PARDriverBase)
if wsapi_version:
mock_client.getWsApiVersion.return_value = (
wsapi_version)
else:
mock_client.getWsApiVersion.return_value = (
self.wsapi_version_latest)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
return mock_client
@mock.patch('hpe3parclient.version', "3.0.9")
def test_unsupported_client_version(self):
self.assertRaises(exception.InvalidInput,
self.setup_driver)
def test_ssh_options(self):
expected_hosts_key_file = "test_hosts_key_file"
self.flags(ssh_hosts_key_file=expected_hosts_key_file,
strict_ssh_host_key_policy=False)
self.ctxt = context.get_admin_context()
mock_client = self.setup_mock_client(
driver=hpefcdriver.HPE3PARFCDriver)
expected = [
mock.call.login(HPE3PAR_USER_NAME, HPE3PAR_USER_PASS),
mock.call.setSSHOptions(
HPE3PAR_SAN_IP,
HPE3PAR_USER_NAME,
HPE3PAR_USER_PASS,
privatekey=HPE3PAR_SAN_SSH_PRIVATE,
known_hosts_file=expected_hosts_key_file,
missing_key_policy="AutoAddPolicy",
port=HPE3PAR_SAN_SSH_PORT,
conn_timeout=HPE3PAR_SAN_SSH_CON_TIMEOUT),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
expected +
self.standard_logout)
def test_ssh_options_strict(self):
expected_hosts_key_file = "test_hosts_key_file"
self.flags(ssh_hosts_key_file=expected_hosts_key_file,
strict_ssh_host_key_policy=True)
self.ctxt = context.get_admin_context()
mock_client = self.setup_mock_client(
driver=hpefcdriver.HPE3PARFCDriver)
expected = [
mock.call.login(HPE3PAR_USER_NAME, HPE3PAR_USER_PASS),
mock.call.setSSHOptions(
HPE3PAR_SAN_IP,
HPE3PAR_USER_NAME,
HPE3PAR_USER_PASS,
privatekey=HPE3PAR_SAN_SSH_PRIVATE,
known_hosts_file=expected_hosts_key_file,
missing_key_policy="RejectPolicy",
port=HPE3PAR_SAN_SSH_PORT,
conn_timeout=HPE3PAR_SAN_SSH_CON_TIMEOUT),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2)]
mock_client.assert_has_calls(expected + self.standard_logout)
def test_task_waiter(self):
task_statuses = [self.STATUS_ACTIVE, self.STATUS_ACTIVE]
def side_effect(*args):
return task_statuses and task_statuses.pop(0) or self.STATUS_DONE
conf = {'getTask.side_effect': side_effect}
mock_client = self.setup_driver(mock_conf=conf)
task_id = 1234
interval = .001
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
waiter = common.TaskWaiter(mock_client, task_id, interval)
status = waiter.wait_for_task()
expected = [
mock.call.getTask(task_id),
mock.call.getTask(task_id),
mock.call.getTask(task_id)
]
mock_client.assert_has_calls(expected)
self.assertEqual(self.STATUS_DONE, status)
def test_create_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.create_volume(self.volume)
comment = Comment({
"display_name": "Foo Volume",
"type": "OpenStack",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"})
expected = [
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': comment,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_create_volume_in_generic_group(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
volume = {'name': self.VOLUME_NAME,
'id': self.VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'group_id': '87101633-13e0-41ee-813b-deabc372267b',
'host': self.FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.create_volume(volume)
comment = Comment({
"display_name": "Foo Volume",
"type": "OpenStack",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"})
expected = [
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': comment,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_create_volume_in_pool(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
return_model = self.driver.create_volume(self.volume_pool)
comment = Comment({
"display_name": "Foo Volume",
"type": "OpenStack",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"})
expected = [
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG2,
2048, {
'comment': comment,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertIsNone(return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_unsupported_dedup_volume_type(self, _mock_volume_types):
mock_client = self.setup_driver(wsapi_version=self.wsapi_version_312)
_mock_volume_types.return_value = {
'name': 'dedup',
'extra_specs': {
'cpg': HPE3PAR_CPG_QOS,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'provisioning': 'dedup',
'volume_type': self.volume_type_dedup}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.assertRaises(exception.InvalidInput,
common.get_volume_settings_from_type_id,
self.VOLUME_TYPE_ID_DEDUP,
"mock")
@mock.patch.object(volume_types, 'get_volume_type')
def test_get_snap_cpg_from_volume_type(self, _mock_volume_types):
mock_client = self.setup_driver()
expected_type_snap_cpg = "type_snap_cpg"
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'cpg': HPE3PAR_CPG,
'snap_cpg': expected_type_snap_cpg,
'volume_type': self.volume_type}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
result = common.get_volume_settings_from_type_id(
"mock", self.driver.configuration.hpe3par_cpg)
self.assertEqual(expected_type_snap_cpg, result['snap_cpg'])
@mock.patch.object(volume_types, 'get_volume_type')
def test_get_snap_cpg_from_volume_type_cpg(self, _mock_volume_types):
mock_client = self.setup_driver()
expected_cpg = 'use_extra_specs_cpg'
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'cpg': expected_cpg,
'volume_type': self.volume_type}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
result = common.get_volume_settings_from_type_id(
"mock", self.driver.configuration.hpe3par_cpg)
self.assertEqual(self.driver.configuration.hpe3par_cpg_snap,
result['snap_cpg'])
@mock.patch.object(volume_types, 'get_volume_type')
def test_get_snap_cpg_from_volume_type_conf_snap_cpg(
self, _mock_volume_types):
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'volume_type': self.volume_type}}
conf = self.setup_configuration()
expected_snap_cpg = conf.hpe3par_cpg_snap
mock_client = self.setup_driver(config=conf)
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
result = common.get_volume_settings_from_type_id(
"mock", self.driver.configuration.hpe3par_cpg)
self.assertEqual(expected_snap_cpg, result['snap_cpg'])
@mock.patch.object(volume_types, 'get_volume_type')
def test_get_snap_cpg_from_volume_type_conf_cpg(
self, _mock_volume_types):
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'volume_type': self.volume_type}}
conf = self.setup_configuration()
conf.hpe3par_cpg_snap = None
expected_cpg = conf.hpe3par_cpg
mock_client = self.setup_driver(config=conf)
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
result = common.get_volume_settings_from_type_id(
"mock", self.driver.configuration.hpe3par_cpg)
self.assertEqual(expected_cpg, result['snap_cpg'])
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_qos(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'cpg': HPE3PAR_CPG_QOS,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
'tdvv': False,
'volume_type': self.volume_type}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
return_model = self.driver.create_volume(self.volume_qos)
comment = Comment({
"volume_type_name": "gold",
"display_name": "Foo Volume",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_type_id": "gold",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"qos": {},
"type": "OpenStack"})
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': comment,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertIsNone(return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_replicated_periodic(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getRemoteCopyGroup.side_effect = (
hpeexceptions.HTTPNotFound)
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
return_model = self.driver.create_volume(self.volume_replicated)
comment = Comment({
"volume_type_name": "replicated",
"display_name": "Foo Volume",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_type_id": "be9181f1-4040-46f2-8298-e7532f2bf9db",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"qos": {},
"type": "OpenStack"})
backend_id = self.replication_targets[0]['backend_id']
expected = [
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': comment,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP}),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createRemoteCopyGroup(
self.RCG_3PAR_NAME,
[{'userCPG': HPE3PAR_CPG_REMOTE,
'targetName': backend_id,
'mode': PERIODIC_MODE,
'snapCPG': HPE3PAR_CPG_REMOTE}],
{'localUserCPG': HPE3PAR_CPG,
'localSnapCPG': HPE3PAR_CPG_SNAP}),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': backend_id}],
optional={'volumeAutoCreation': True}),
mock.call.modifyRemoteCopyGroup(
self.RCG_3PAR_NAME,
{'targets': [{'syncPeriod': SYNC_PERIOD,
'targetName': backend_id}]}),
mock.call.startRemoteCopy(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual({'replication_status': 'enabled',
'provider_location': self.CLIENT_ID},
return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_delete_volume_replicated_failedover(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getRemoteCopyGroup.return_value = (
{'targets': [{'targetName': 'tgt'}]})
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
volume = self.volume_replicated.copy()
volume['replication_status'] = 'failed-over'
self.driver.delete_volume(volume)
rcg_name = self.RCG_3PAR_NAME + ".r" + self.CLIENT_ID
expected = [
mock.call.getRemoteCopyGroup(rcg_name),
mock.call.toggleRemoteCopyConfigMirror(
'tgt',
mirror_config=False),
mock.call.stopRemoteCopy(rcg_name),
mock.call.removeVolumeFromRemoteCopyGroup(
rcg_name,
self.VOLUME_3PAR_NAME,
removeFromTarget=True),
mock.call.removeRemoteCopyGroup(rcg_name),
mock.call.deleteVolume(self.VOLUME_3PAR_NAME),
mock.call.toggleRemoteCopyConfigMirror(
'tgt',
mirror_config=True)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_replicated_sync(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
self.replication_targets[0]['quorum_witness_ip'] = None
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getRemoteCopyGroup.side_effect = (
hpeexceptions.HTTPNotFound)
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
return_model = self.driver.create_volume(self.volume_replicated)
comment = Comment({
"volume_type_name": "replicated",
"display_name": "Foo Volume",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_type_id": "be9181f1-4040-46f2-8298-e7532f2bf9db",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"qos": {},
"type": "OpenStack"})
backend_id = self.replication_targets[0]['backend_id']
expected = [
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': comment,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP}),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createRemoteCopyGroup(
self.RCG_3PAR_NAME,
[{'userCPG': HPE3PAR_CPG_REMOTE,
'targetName': backend_id,
'mode': SYNC_MODE,
'snapCPG': HPE3PAR_CPG_REMOTE}],
{'localUserCPG': HPE3PAR_CPG,
'localSnapCPG': HPE3PAR_CPG_SNAP}),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': backend_id}],
optional={'volumeAutoCreation': True}),
mock.call.startRemoteCopy(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual({'replication_status': 'enabled',
'provider_location': self.CLIENT_ID},
return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_replicated_peer_persistence(
self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
self.replication_targets[0]['quorum_witness_ip'] = '10.50.3.192'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getRemoteCopyGroup.side_effect = (
hpeexceptions.HTTPNotFound)
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
return_model = self.driver.create_volume(self.volume_replicated)
comment = Comment({
"volume_type_name": "replicated",
"display_name": "Foo Volume",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_type_id": "be9181f1-4040-46f2-8298-e7532f2bf9db",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"qos": {},
"type": "OpenStack"})
backend_id = self.replication_targets[0]['backend_id']
expected = [
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': comment,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP}),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createRemoteCopyGroup(
self.RCG_3PAR_NAME,
[{'userCPG': HPE3PAR_CPG_REMOTE,
'targetName': backend_id,
'mode': SYNC_MODE,
'snapCPG': HPE3PAR_CPG_REMOTE}],
{'localUserCPG': HPE3PAR_CPG,
'localSnapCPG': HPE3PAR_CPG_SNAP}),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': backend_id}],
optional={'volumeAutoCreation': True}),
mock.call.modifyRemoteCopyGroup(
self.RCG_3PAR_NAME,
{'targets': [
{'policies': {'autoFailover': True,
'pathManagement': True,
'autoRecover': True}}]}),
mock.call.startRemoteCopy(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual({'replication_status': 'enabled',
'provider_location': self.CLIENT_ID},
return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_dedup_compression(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
_mock_volume_types.return_value = {
'name': 'dedup_compression',
'extra_specs': {
'cpg': HPE3PAR_CPG_QOS,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'hpe3par:provisioning': 'dedup',
'hpe3par:compression': 'True',
'volume_type': self.volume_type_dedup_compression}}
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234',
'licenseInfo': {
'licenses': [{'name': 'Compression'},
{'name': 'Thin Provisioning (102400G)'},
{'name': 'System Reporter'}]
}
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
return_model = self.driver.create_volume(
self.volume_dedup_compression)
comment = Comment({
"volume_type_name": "dedup_compression",
"display_name": "Foo Volume",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_type_id": "d03338a9-9115-48a3-8dfc-33333333333",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"qos": {},
"type": "OpenStack"})
expectedcall = [
mock.call.getStorageSystemInfo()]
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getStorageSystemInfo(),
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
16384, {
'comment': comment,
'tpvv': False,
'tdvv': True,
'compression': True,
'snapCPG': HPE3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
self.standard_login +
expectedcall +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertIsNone(return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_dedup(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
_mock_volume_types.return_value = {
'name': 'dedup',
'extra_specs': {
'cpg': HPE3PAR_CPG_QOS,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'provisioning': 'dedup',
'volume_type': self.volume_type_dedup}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
return_model = self.driver.create_volume(self.volume_dedup)
comment = Comment({
"volume_type_name": "dedup",
"display_name": "Foo Volume",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_type_id": "d03338a9-9115-48a3-8dfc-11111111111",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"qos": {},
"type": "OpenStack"})
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': comment,
'tpvv': False,
'tdvv': True,
'snapCPG': HPE3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertIsNone(return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_flash_cache(self, _mock_volume_types):
# Setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
_mock_volume_types.return_value = {
'name': 'flash-cache-on',
'extra_specs': {
'cpg': HPE3PAR_CPG2,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
'tdvv': False,
'hpe3par:flash_cache': 'true',
'volume_type': self.volume_type_flash_cache}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
mock_client.FLASH_CACHE_ENABLED = FLASH_CACHE_ENABLED
mock_client.FLASH_CACHE_DISABLED = FLASH_CACHE_DISABLED
return_model = self.driver.create_volume(self.volume_flash_cache)
comment = Comment({
"volume_type_name": "flash-cache-on",
"display_name": "Foo Volume",
"name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"volume_type_id": "d03338a9-9115-48a3-8dfc-22222222222",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
"qos": {}, "type": "OpenStack"})
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': comment,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP}),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet('vvs-0DM4qZEVSKON-DXN-NwVpw', None),
mock.call.createQoSRules(
'vvs-0DM4qZEVSKON-DXN-NwVpw',
{'priority': 2}
),
mock.call.modifyVolumeSet(
'vvs-0DM4qZEVSKON-DXN-NwVpw', flashCachePolicy=1),
mock.call.addVolumeToVolumeSet(
'vvs-0DM4qZEVSKON-DXN-NwVpw',
'osv-0DM4qZEVSKON-DXN-NwVpw')]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertIsNone(return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_unsupported_flash_cache_volume(self, _mock_volume_types):
mock_client = self.setup_driver(wsapi_version=self.wsapi_version_312)
_mock_volume_types.return_value = {
'name': 'flash-cache-on',
'extra_specs': {
'cpg': HPE3PAR_CPG2,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
'tdvv': False,
'hpe3par:flash_cache': 'true',
'volume_type': self.volume_type_flash_cache}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.assertRaises(exception.InvalidInput,
common.get_flash_cache_policy,
self.flash_cache_3par_keys)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_not_3par(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
mock_client.getVolumeSnapshots.return_value = []
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(exception.InvalidHost,
self.driver.retype,
self.ctxt,
self.RETYPE_VOLUME_INFO_0,
self.RETYPE_VOLUME_TYPE_1,
self.RETYPE_DIFF,
self.RETYPE_HOST_NOT3PAR)
expected = [mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
mock.call.getVolume(self.VOLUME_3PAR_NAME)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_volume_not_found(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
mock_client.getVolumeSnapshots.return_value = []
mock_client.getVolume.side_effect = hpeexceptions.HTTPNotFound
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(hpeexceptions.HTTPNotFound,
self.driver.retype,
self.ctxt,
self.RETYPE_VOLUME_INFO_0,
self.RETYPE_VOLUME_TYPE_1,
self.RETYPE_DIFF,
self.RETYPE_HOST)
expected = [mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
mock.call.getVolume(self.VOLUME_3PAR_NAME)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_specs_error_reverts_snap_cpg(self, _mock_volume_types):
_mock_volume_types.side_effect = [
self.RETYPE_VOLUME_TYPE_1, self.RETYPE_VOLUME_TYPE_0]
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
mock_client.getVolume.return_value = self.RETYPE_VOLUME_INFO_0
# Fail the QOS setting to test the revert of the snap CPG rename.
mock_client.addVolumeToVolumeSet.side_effect = \
hpeexceptions.HTTPForbidden
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(hpeexceptions.HTTPForbidden,
self.driver.retype,
self.ctxt,
{'id': self.VOLUME_ID},
self.RETYPE_VOLUME_TYPE_0,
self.RETYPE_DIFF,
self.RETYPE_HOST)
old_settings = {
'snapCPG': self.RETYPE_VOLUME_INFO_0['snapCPG'],
'comment': self.RETYPE_VOLUME_INFO_0['comment']}
new_settings = {
'snapCPG': (
self.RETYPE_VOLUME_TYPE_1['extra_specs']['snap_cpg']),
'comment': mock.ANY}
expected = [
mock.call.modifyVolume(self.VOLUME_3PAR_NAME, new_settings)
]
mock_client.assert_has_calls(expected)
expected = [
mock.call.modifyVolume(self.VOLUME_3PAR_NAME, old_settings)
]
mock_client.assert_has_calls(expected + self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_revert_comment(self, _mock_volume_types):
_mock_volume_types.side_effect = [
self.RETYPE_VOLUME_TYPE_2, self.RETYPE_VOLUME_TYPE_1]
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
mock_client.getVolume.return_value = self.RETYPE_VOLUME_INFO_1
# Fail the QOS setting to test the revert of the snap CPG rename.
mock_client.deleteVolumeSet.side_effect = hpeexceptions.HTTPForbidden
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(hpeexceptions.HTTPForbidden,
self.driver.retype,
self.ctxt,
{'id': self.VOLUME_ID},
self.RETYPE_VOLUME_TYPE_2,
self.RETYPE_DIFF,
self.RETYPE_HOST)
original = {
'snapCPG': self.RETYPE_VOLUME_INFO_1['snapCPG'],
'comment': self.RETYPE_VOLUME_INFO_1['comment']}
expected = [
mock.call.modifyVolume('osv-0DM4qZEVSKON-DXN-NwVpw', original)]
mock_client.assert_has_calls(expected + self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_different_array(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
mock_client.getVolumeSnapshots.return_value = []
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': 'XXXXXXX'}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(exception.InvalidHost,
self.driver.retype,
self.ctxt,
self.RETYPE_VOLUME_INFO_0,
self.RETYPE_VOLUME_TYPE_1,
self.RETYPE_DIFF,
self.RETYPE_HOST)
expected = [
mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getStorageSystemInfo()]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_across_cpg_domains(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
mock_client.getVolumeSnapshots.return_value = []
mock_client.getCPG.side_effect = [
{'domain': 'domain1'},
{'domain': 'domain2'},
]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(hpecommon.Invalid3PARDomain,
self.driver.retype,
self.ctxt,
self.RETYPE_VOLUME_INFO_0,
self.RETYPE_VOLUME_TYPE_1,
self.RETYPE_DIFF,
self.RETYPE_HOST)
expected = [
mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getStorageSystemInfo(),
mock.call.getCPG(self.RETYPE_VOLUME_INFO_0['userCPG']),
mock.call.getCPG(
self.RETYPE_VOLUME_TYPE_1['extra_specs']['cpg'])
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_across_snap_cpg_domains(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
mock_client.getVolumeSnapshots.return_value = []
mock_client.getCPG.side_effect = [
{'domain': 'cpg_domain'},
{'domain': 'cpg_domain'},
{'domain': 'snap_cpg_domain_1'},
]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(hpecommon.Invalid3PARDomain,
self.driver.retype,
self.ctxt,
self.RETYPE_VOLUME_INFO_0,
self.RETYPE_VOLUME_TYPE_1,
self.RETYPE_DIFF,
self.RETYPE_HOST)
expected = [
mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getStorageSystemInfo(),
mock.call.getCPG(self.RETYPE_VOLUME_INFO_0['userCPG']),
mock.call.getCPG(
self.RETYPE_VOLUME_TYPE_1['extra_specs']['cpg']),
mock.call.getCPG(
self.RETYPE_VOLUME_TYPE_1['extra_specs']['snap_cpg'])
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_to_bad_persona(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_BAD_PERSONA
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
mock_client.getVolumeSnapshots.return_value = []
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(exception.InvalidInput,
self.driver.retype,
self.ctxt,
self.RETYPE_VOLUME_INFO_0,
self.RETYPE_VOLUME_TYPE_BAD_PERSONA,
self.RETYPE_DIFF,
self.RETYPE_HOST)
expected = [mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
mock.call.getVolume(self.VOLUME_3PAR_NAME)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_tune(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS)
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:maxIOPS": "100",
"qos:maxBWS": "50",
"qos:minIOPS": "10",
"qos:minBWS": "20",
"qos:latency": "5",
"qos:priority": "high"})
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
volume = {'id': HPE3PARBaseDriver.CLONE_ID}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
retyped = self.driver.retype(
self.ctxt, volume, type_ref, None, self.RETYPE_HOST)
self.assertTrue(retyped[0])
expected = [
mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA',
{'comment': mock.ANY,
'snapCPG': 'OpenStackCPGSnap'}),
mock.call.deleteVolumeSet('vvs-0DM4qZEVSKON-AAAAAAAAA'),
mock.call.addVolumeToVolumeSet('myvvs',
'osv-0DM4qZEVSKON-AAAAAAAAA'),
mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA',
{'action': 6,
'userCPG': 'OpenStackCPG',
'conversionOperation': 1,
'compression': False,
'tuneOperation': 1}),
mock.call.getTask(1),
]
mock_client.assert_has_calls(expected + self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_non_rep_type_to_rep_type(self, _mock_volume_types):
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getRemoteCopyGroup.side_effect = (
hpeexceptions.HTTPNotFound)
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = {
'id': self.REPLICATION_CLIENT_ID,
'serialNumber': '1234567'
}
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
mock_client.getVolume.return_value = {
'name': mock.ANY,
'comment': "{'display_name': 'Foo Volume'}",
'provisioningType': mock.ANY,
'userCPG': 'OpenStackCPG',
'snapCPG': 'OpenStackCPGSnap'}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
retyped = self.driver.retype(
self.ctxt,
self.volume,
self.volume_type_replicated,
None,
self.RETYPE_HOST)
self.assertTrue(retyped[0])
backend_id = self.replication_targets[0]['backend_id']
expected = [
mock.call.createRemoteCopyGroup(
self.RCG_3PAR_NAME,
[{'userCPG': HPE3PAR_CPG_REMOTE,
'targetName': backend_id,
'mode': PERIODIC_MODE,
'snapCPG': HPE3PAR_CPG_REMOTE}],
{'localUserCPG': HPE3PAR_CPG,
'localSnapCPG': HPE3PAR_CPG_SNAP}),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': backend_id}],
optional={'volumeAutoCreation': True}),
mock.call.modifyRemoteCopyGroup(
self.RCG_3PAR_NAME,
{'targets': [{'syncPeriod': SYNC_PERIOD,
'targetName': backend_id}]}),
mock.call.startRemoteCopy(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(expected + self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_rep_type_to_non_rep_type(self, _mock_volume_types):
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getRemoteCopyGroup.side_effect = (
hpeexceptions.HTTPNotFound)
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = {
'id': self.REPLICATION_CLIENT_ID,
'serialNumber': '1234567'
}
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
volume_1 = {'name': self.VOLUME_NAME,
'id': self.VOLUME_ID,
'display_name': 'Foo Volume',
'replication_status': 'disabled',
'provider_location': self.CLIENT_ID,
'size': 2,
'host': self.FAKE_CINDER_HOST,
'volume_type': 'replicated',
'volume_type_id': 'gold'}
volume_type = {'name': 'replicated',
'deleted': False,
'updated_at': None,
'deleted_at': None,
'extra_specs': {'replication_enabled': 'False'},
'id': 'silver'}
def get_side_effect(*args):
data = {'value': None}
if args[1] == 'gold':
data['value'] = {
'name': 'replicated',
'id': 'gold',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
elif args[1] == 'silver':
data['value'] = {'name': 'silver',
'deleted': False,
'updated_at': None,
'extra_specs': {
'replication_enabled': 'False'},
'deleted_at': None,
'id': 'silver'}
return data['value']
_mock_volume_types.side_effect = get_side_effect
mock_client.getVolume.return_value = {
'name': mock.ANY,
'comment': "{'display_name': 'Foo Volume'}",
'provisioningType': mock.ANY,
'userCPG': 'OpenStackCPG',
'snapCPG': 'OpenStackCPGSnap'}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
retyped = self.driver.retype(
self.ctxt, volume_1, volume_type, None, self.RETYPE_HOST)
self.assertTrue(retyped[0])
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_NAME),
mock.call.removeVolumeFromRemoteCopyGroup(
self.RCG_3PAR_NAME,
self.VOLUME_3PAR_NAME,
removeFromTarget=True),
mock.call.removeRemoteCopyGroup(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout, any_order=True)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_rep_type_to_rep_type(self, _mock_volume_types):
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getRemoteCopyGroup.side_effect = (
hpeexceptions.HTTPNotFound)
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = {
'id': self.REPLICATION_CLIENT_ID,
'serialNumber': '1234567'
}
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
volume_1 = {'name': self.VOLUME_NAME,
'id': self.VOLUME_ID,
'display_name': 'Foo Volume',
'replication_status': 'disabled',
'provider_location': self.CLIENT_ID,
'size': 2,
'host': self.FAKE_CINDER_HOST,
'volume_type': 'replicated',
'volume_type_id': 'gold'}
volume_type = {'name': 'replicated',
'deleted': False,
'updated_at': None,
'deleted_at': None,
'extra_specs': {'replication_enabled': '<is> True'},
'id': 'silver'}
def get_side_effect(*args):
data = {'value': None}
if args[1] == 'gold':
data['value'] = {
'name': 'replicated',
'id': 'gold',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
elif args[1] == 'silver':
data['value'] = {
'name': 'silver',
'deleted': False,
'updated_at': None,
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '1500',
'volume_type': self.volume_type_replicated},
'deleted_at': None,
'id': 'silver'}
return data['value']
_mock_volume_types.side_effect = get_side_effect
mock_client.getVolume.return_value = {
'name': mock.ANY,
'comment': "{'display_name': 'Foo Volume'}",
'provisioningType': mock.ANY,
'userCPG': 'OpenStackCPG',
'snapCPG': 'OpenStackCPGSnap'}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
backend_id = self.replication_targets[0]['backend_id']
retyped = self.driver.retype(
self.ctxt, volume_1, volume_type, None, self.RETYPE_HOST)
self.assertTrue(retyped[0])
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_NAME),
mock.call.removeVolumeFromRemoteCopyGroup(
self.RCG_3PAR_NAME,
self.VOLUME_3PAR_NAME,
removeFromTarget=True),
mock.call.removeRemoteCopyGroup(self.RCG_3PAR_NAME),
mock.call.createRemoteCopyGroup(
self.RCG_3PAR_NAME,
[{'userCPG': HPE3PAR_CPG_REMOTE,
'targetName': backend_id,
'mode': PERIODIC_MODE,
'snapCPG': HPE3PAR_CPG_REMOTE}],
{'localUserCPG': HPE3PAR_CPG,
'localSnapCPG': HPE3PAR_CPG_SNAP}),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': backend_id}],
optional={'volumeAutoCreation': True}),
mock.call.startRemoteCopy(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout, any_order=True)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_qos_spec(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
cpg = "any_cpg"
snap_cpg = "any_cpg"
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
common._retype(self.volume,
HPE3PARBaseDriver.VOLUME_3PAR_NAME,
"old_type", "old_type_id",
HPE3PARBaseDriver.RETYPE_HOST,
None, cpg, cpg, snap_cpg, snap_cpg,
True, False, False, True, None, None,
self.QOS_SPECS, self.RETYPE_QOS_SPECS,
None, None,
"{}", None)
expected = [
mock.call.createVolumeSet('vvs-0DM4qZEVSKON-DXN-NwVpw', None),
mock.call.createQoSRules(
'vvs-0DM4qZEVSKON-DXN-NwVpw',
{'ioMinGoal': 100, 'ioMaxLimit': 1000,
'bwMinGoalKB': 25600, 'bwMaxLimitKB': 51200,
'priority': 3,
'latencyGoal': 25}
),
mock.call.addVolumeToVolumeSet(
'vvs-0DM4qZEVSKON-DXN-NwVpw',
'osv-0DM4qZEVSKON-DXN-NwVpw')]
mock_client.assert_has_calls(expected)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_dedup(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_3
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
cpg = "any_cpg"
snap_cpg = "any_cpg"
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
common._retype(self.volume,
HPE3PARBaseDriver.VOLUME_3PAR_NAME,
"old_type", "old_type_id",
HPE3PARBaseDriver.RETYPE_HOST,
None, cpg, cpg, snap_cpg, snap_cpg,
True, False, False, True, None, None,
self.QOS_SPECS, self.RETYPE_QOS_SPECS,
None, None,
"{}", None)
expected = [
mock.call.addVolumeToVolumeSet(u'vvs-0DM4qZEVSKON-DXN-NwVpw',
'osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.modifyVolume('osv-0DM4qZEVSKON-DXN-NwVpw',
{'action': 6,
'userCPG': 'any_cpg',
'conversionOperation': 3,
'compression': False,
'tuneOperation': 1}),
mock.call.getTask(1)]
mock_client.assert_has_calls(expected)
@ddt.data('volume', 'volume_name_id')
def test_delete_volume(self, volume_attr):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.delete_volume(getattr(self, volume_attr))
name_3par = getattr(self, volume_attr.upper() + '_3PAR_NAME')
expected = [mock.call.deleteVolume(name_3par)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_delete_volume_online_clone_active(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
ex = hpeexceptions.HTTPConflict("Online clone is active.")
ex._error_code = 151
mock_client.deleteVolume = mock.Mock(side_effect=ex)
mock_client.isOnlinePhysicalCopy.return_value = True
self.driver.delete_volume(self.volume)
expected = [
mock.call.deleteVolume(self.VOLUME_3PAR_NAME),
mock.call.isOnlinePhysicalCopy(self.VOLUME_3PAR_NAME),
mock.call.stopOnlinePhysicalCopy(self.VOLUME_3PAR_NAME)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def _FakeRetrying(wait_func=None,
original_retrying=hpecommon.utils.retrying.Retrying,
*args, **kwargs):
return original_retrying(wait_func=lambda *a, **k: 0,
*args, **kwargs)
@mock.patch('retrying.Retrying', _FakeRetrying)
def test_delete_volume_online_active_done(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
ex = hpeexceptions.HTTPConflict("Online clone is active.")
ex._error_code = 151
mock_client.deleteVolume = mock.Mock(side_effect=[ex, 200])
mock_client.isOnlinePhysicalCopy.return_value = False
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.delete_volume(self.volume)
expected = [
mock.call.deleteVolume(self.VOLUME_3PAR_NAME),
mock.call.isOnlinePhysicalCopy(self.VOLUME_3PAR_NAME),
mock.call.deleteVolume(self.VOLUME_3PAR_NAME)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_delete_volume_replicated(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
ex = hpeexceptions.HTTPConflict("In use")
ex._error_code = 34
mock_client.deleteVolume = mock.Mock(side_effect=[ex, 200])
mock_client.findVolumeSet.return_value = self.VVS_NAME
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'cpg': HPE3PAR_CPG_QOS,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.delete_volume(self.volume_replicated)
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_NAME),
mock.call.removeVolumeFromRemoteCopyGroup(
self.RCG_3PAR_NAME,
self.VOLUME_3PAR_NAME,
removeFromTarget=True),
mock.call.removeRemoteCopyGroup(self.RCG_3PAR_NAME),
mock.call.deleteVolume(self.VOLUME_3PAR_NAME),
mock.call.findVolumeSet(self.VOLUME_3PAR_NAME),
mock.call.removeVolumeFromVolumeSet(self.VVS_NAME,
self.VOLUME_3PAR_NAME),
mock.call.deleteVolume(self.VOLUME_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_get_cpg_with_volume_return_usercpg(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'name': mock.ANY,
'userCPG': HPE3PAR_CPG2}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {'id': HPE3PARBaseDriver.VOLUME_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'display_name': 'Foo Volume',
'size': 2,
'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2)}
common = self.driver._login()
user_cpg = common.get_cpg(volume)
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume['id'])
self.assertEqual(HPE3PAR_CPG2, user_cpg)
expected = [mock.call.getVolume(vol_name)]
mock_client.assert_has_calls(
self.standard_login +
expected)
def test_get_cpg_with_volume_return_snapcpg(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'name': mock.ANY,
'snapCPG': HPE3PAR_CPG2}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {'id': HPE3PARBaseDriver.VOLUME_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'display_name': 'Foo Volume',
'size': 2,
'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2)}
common = self.driver._login()
snap_cpg = common.get_cpg(volume, allowSnap=True)
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume['id'])
self.assertEqual(HPE3PAR_CPG2, snap_cpg)
expected = [mock.call.getVolume(vol_name)]
mock_client.assert_has_calls(
self.standard_login +
expected)
def test_get_cpg_with_volume_return_no_cpg(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'name': mock.ANY}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {'id': HPE3PARBaseDriver.VOLUME_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'display_name': 'Foo Volume',
'size': 2,
'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2)}
common = self.driver._login()
cpg_name = common.get_cpg(volume)
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume['id'])
self.assertEqual(HPE3PAR_CPG2, cpg_name)
expected = [mock.call.getVolume(vol_name)]
mock_client.assert_has_calls(
self.standard_login +
expected)
@ddt.data('volume', 'volume_name_id')
def test_create_cloned_volume(self, volume_attr):
src_vref = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'name': mock.ANY}
mock_client.copyVolume.return_value = {'taskid': 1}
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': 'XXXXXXX'}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2),
'source_volid': src_vref.id}
model_update = self.driver.create_cloned_volume(volume, src_vref)
self.assertIsNone(model_update)
# snapshot name is random
snap_name = mock.ANY
optional = mock.ANY
expectedcall = [
mock.call.getStorageSystemInfo()]
expected = [
mock.call.createSnapshot(snap_name, vol_name, optional),
mock.call.getVolume(snap_name),
mock.call.copyVolume(
snap_name,
'osv-0DM4qZEVSKON-AAAAAAAAA',
HPE3PAR_CPG2,
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
'tdvv': False, 'online': True})]
mock_client.assert_has_calls(
self.standard_login +
expectedcall +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_clone_volume_with_vvs(self, _mock_volume_types):
# Setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
mock_client = self.setup_driver(config=conf)
_mock_volume_types.return_value = {
'name': 'gold',
'id': 'gold-id',
'extra_specs': {'vvs': self.VVS_NAME}}
mock_client.getVolume.return_value = {'name': mock.ANY}
mock_client.copyVolume.return_value = {'taskid': 1}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume_vvs = {'id': self.CLONE_ID,
'name': self.VOLUME_NAME,
'display_name': 'Foo Volume',
'size': 2,
'host': self.FAKE_CINDER_HOST,
'volume_type': 'gold',
'volume_type_id': 'gold-id'}
src_vref = {'id': self.VOLUME_ID,
'name': self.VOLUME_NAME,
'size': 2, 'status': 'available',
'volume_type': 'gold',
'host': self.FAKE_CINDER_HOST,
'volume_type_id': 'gold-id'}
# creation of the temp snapshot
common = hpecommon.HPE3PARCommon(conf)
snap_name = mock.ANY
vol_name = common._get_3par_vol_name(src_vref['id'])
optional = mock.ANY
model_update = self.driver.create_cloned_volume(volume_vvs,
src_vref)
self.assertIsNone(model_update)
clone_vol_vvs = common.get_volume_settings_from_type(volume_vvs)
source_vol_vvs = common.get_volume_settings_from_type(src_vref)
self.assertEqual(clone_vol_vvs, source_vol_vvs)
expected = [
mock.call.createSnapshot(snap_name, vol_name, optional),
mock.call.getVolume(snap_name),
mock.call.copyVolume(
snap_name,
'osv-0DM4qZEVSKON-AAAAAAAAA',
'OpenStackCPG',
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
'tdvv': False, 'online': True}),
mock.call.addVolumeToVolumeSet(
self.VVS_NAME,
'osv-0DM4qZEVSKON-AAAAAAAAA')]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_backup_iscsi_volume_with_chap_disabled(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'name': mock.ANY}
mock_client.copyVolume.return_value = {'taskid': 1}
mock_client.getVolumeMetaData.side_effect = hpeexceptions.HTTPNotFound
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2)}
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'size': 2, 'status': 'backing-up'}
model_update = self.driver.create_cloned_volume(volume, src_vref)
self.assertIsNone(model_update)
# creation of the temp snapshot
common = hpecommon.HPE3PARCommon(None)
snap_name = mock.ANY
vol_name = common._get_3par_vol_name(src_vref['id'])
optional = mock.ANY
expected = [
mock.call.createSnapshot(snap_name, vol_name, optional),
mock.call.getVolume(snap_name),
mock.call.copyVolume(
snap_name,
'osv-0DM4qZEVSKON-AAAAAAAAA',
HPE3PAR_CPG2,
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
'tdvv': False, 'online': True})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_create_clone_iscsi_volume_with_chap_disabled(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
mock_client.getVolume.return_value = {'name': mock.ANY}
mock_client.copyVolume.return_value = {'taskid': 1}
mock_client.getVolumeMetaData.side_effect = hpeexceptions.HTTPNotFound
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2)}
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'size': 2, 'status': 'available'}
model_update = self.driver.create_cloned_volume(volume, src_vref)
self.assertIsNone(model_update)
common = hpecommon.HPE3PARCommon(None)
snap_name = mock.ANY
vol_name = common._get_3par_vol_name(src_vref['id'])
optional = mock.ANY
expected = [
mock.call.getVolumeMetaData(vol_name,
'HPQ-cinder-CHAP-name'),
mock.call.createSnapshot(snap_name, vol_name, optional),
mock.call.getVolume(snap_name),
mock.call.copyVolume(
snap_name,
'osv-0DM4qZEVSKON-AAAAAAAAA',
HPE3PAR_CPG2,
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
'tdvv': False, 'online': True})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_backup_iscsi_volume_with_chap_enabled(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
mock_client.getVolume.return_value = {'name': mock.ANY}
task_id = 1
mock_client.copyVolume.return_value = {'taskid': task_id}
mock_client.getVolumeMetaData.return_value = {
'value': 'random-key'}
mock_client.getTask.return_value = {'status': 1}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 5,
'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2),
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'size': 5, 'status': 'backing-up'}
model_update = self.driver.create_cloned_volume(volume, src_vref)
self.assertIsNone(model_update)
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume['id'])
src_vol_name = common._get_3par_vol_name(src_vref['id'])
optional = {'priority': 1}
comment = mock.ANY
expected = [
mock.call.getVolumeMetaData(src_vol_name,
'HPQ-cinder-CHAP-name'),
mock.call.createVolume(vol_name, 'fakepool',
5120, comment),
mock.call.copyVolume(
src_vol_name,
vol_name,
None,
optional=optional),
mock.call.getTask(task_id),
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_create_cloned_volume_offline_copy(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'name': mock.ANY}
task_id = 1
mock_client.copyVolume.return_value = {'taskid': task_id}
mock_client.getTask.return_value = {'status': 1}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 5,
'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2),
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'size': 2, 'status': 'available'}
model_update = self.driver.create_cloned_volume(volume, src_vref)
self.assertIsNone(model_update)
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume['id'])
src_vol_name = common._get_3par_vol_name(src_vref['id'])
optional = {'priority': 1}
comment = mock.ANY
expected = [
mock.call.createVolume(vol_name, 'fakepool',
5120, comment),
mock.call.copyVolume(
src_vol_name,
vol_name,
None,
optional=optional),
mock.call.getTask(task_id),
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_cloned_qos_volume(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_2
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'name': mock.ANY}
mock_client.copyVolume.return_value = {'taskid': 1}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
src_vref = {'id': HPE3PARBaseDriver.CLONE_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'size': 2, 'status': 'available'}
volume = self.volume_qos.copy()
host = "TEST_HOST"
pool = "TEST_POOL"
volume_host = volume_utils.append_host(host, pool)
expected_cpg = pool
volume['id'] = HPE3PARBaseDriver.VOLUME_ID
volume['host'] = volume_host
volume['source_volid'] = HPE3PARBaseDriver.CLONE_ID
model_update = self.driver.create_cloned_volume(volume, src_vref)
self.assertIsNone(model_update)
# creation of the temp snapshot
common = hpecommon.HPE3PARCommon(None)
snap_name = mock.ANY
vol_name = common._get_3par_vol_name(src_vref['id'])
optional = mock.ANY
expected = [
mock.call.getCPG(expected_cpg),
mock.call.createSnapshot(snap_name, vol_name, optional),
mock.call.getVolume(snap_name),
mock.call.copyVolume(
snap_name,
self.VOLUME_3PAR_NAME,
expected_cpg,
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
'tdvv': False, 'online': True}),
mock.call.addVolumeToVolumeSet(
'yourvvs',
'osv-0DM4qZEVSKON-DXN-NwVpw')]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_migrate_volume(self):
conf = {
'getStorageSystemInfo.return_value': {
'id': self.CLIENT_ID,
'serialNumber': '1234'},
'getTask.return_value': {
'status': 1},
'getCPG.return_value': {},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': self.RETYPE_VOLUME_INFO_1
}
mock_client = self.setup_driver(mock_conf=conf)
mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'volume_type_id': None,
'size': 2,
'status': 'available',
'host': HPE3PARBaseDriver.FAKE_HOST,
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume_name_3par = common._encode_name(volume['id'])
loc_info = 'HPE3PARDriver:1234:CPG-FC1'
host = {'host': 'stack@3parfc1#CPG-FC1',
'capabilities': {'location_info': loc_info}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((True, None), result)
osv_matcher = 'osv-' + volume_name_3par
comment = Comment({
"display_name": "Foo Volume",
"qos": {},
})
expected = [
mock.call.modifyVolume(
osv_matcher,
{'comment': comment,
'snapCPG': HPE3PAR_CPG_SNAP}),
mock.call.modifyVolume(osv_matcher,
{'action': 6,
'userCPG': 'CPG-FC1',
'conversionOperation': 1,
'compression': False,
'tuneOperation': 1}),
mock.call.getTask(mock.ANY)
]
mock_client.assert_has_calls(expected + self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_migrate_volume_with_type(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_2
conf = {
'getStorageSystemInfo.return_value': {
'id': self.CLIENT_ID,
'serialNumber': '1234'},
'getTask.return_value': {
'status': 1},
'getCPG.return_value': {},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': self.RETYPE_VOLUME_INFO_1
}
mock_client = self.setup_driver(mock_conf=conf)
mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
display_name = 'Foo Volume'
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': display_name,
"volume_type_id": self.RETYPE_VOLUME_TYPE_2['id'],
'size': 2,
'status': 'available',
'host': HPE3PARBaseDriver.FAKE_HOST,
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume_name_3par = common._encode_name(volume['id'])
loc_info = 'HPE3PARDriver:1234:CPG-FC1'
instance_host = 'stack@3parfc1#CPG-FC1'
host = {'host': instance_host,
'capabilities': {'location_info': loc_info}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
# when the host and pool are the same we'll get None
self.assertEqual((True, None), result)
osv_matcher = 'osv-' + volume_name_3par
expected_comment = Comment({
"display_name": display_name,
"volume_type_id": self.RETYPE_VOLUME_TYPE_2['id'],
"volume_type_name": self.RETYPE_VOLUME_TYPE_2['name'],
"vvs": self.RETYPE_VOLUME_TYPE_2['extra_specs']['vvs']
})
expected = [
mock.call.modifyVolume(
osv_matcher,
{'comment': expected_comment,
'snapCPG': self.RETYPE_VOLUME_TYPE_2
['extra_specs']['snap_cpg']}),
mock.call.modifyVolume(
osv_matcher,
{'action': 6,
'userCPG': 'CPG-FC1',
'conversionOperation': 1,
'tuneOperation': 1,
'compression': False}),
mock.call.getTask(mock.ANY)
]
mock_client.assert_has_calls(
expected +
self.standard_logout)
def test_migrate_volume_diff_host(self):
conf = {
'getStorageSystemInfo.return_value': {
'id': self.CLIENT_ID,
'serialNumber': 'different'},
}
mock_client = self.setup_driver(mock_conf=conf)
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'volume_type_id': None,
'size': 2,
'status': 'available',
'host': HPE3PARBaseDriver.FAKE_HOST,
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
loc_info = 'HPE3PARDriver:1234:CPG-FC1'
host = {'host': 'stack@3parfc1',
'capabilities': {'location_info': loc_info}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((False, None), result)
@mock.patch.object(volume_types, 'get_volume_type')
def test_migrate_volume_diff_domain(self, _mock_volume_types):
_mock_volume_types.return_value = self.volume_type
conf = {
'getStorageSystemInfo.return_value': {
'id': self.CLIENT_ID,
'serialNumber': '1234'},
'getTask.return_value': {
'status': 1},
'getCPG.return_value': {},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': self.RETYPE_VOLUME_INFO_1
}
mock_client = self.setup_driver(mock_conf=conf)
mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'volume_type_id': None,
'size': 2,
'status': 'available',
'host': HPE3PARBaseDriver.FAKE_HOST,
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume_name_3par = common._encode_name(volume['id'])
loc_info = 'HPE3PARDriver:1234:CPG-FC1'
host = {'host': 'stack@3parfc1#CPG-FC1',
'capabilities': {'location_info': loc_info}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((True, None), result)
osv_matcher = 'osv-' + volume_name_3par
comment = Comment({"qos": {}, "display_name": "Foo Volume"})
expected = [
mock.call.modifyVolume(
osv_matcher,
{'comment': comment,
'snapCPG': HPE3PAR_CPG_SNAP}),
mock.call.modifyVolume(osv_matcher,
{'action': 6,
'userCPG': 'CPG-FC1',
'conversionOperation': 1,
'tuneOperation': 1,
'compression': False}),
mock.call.getTask(mock.ANY),
]
mock_client.assert_has_calls(expected + self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_migrate_volume_attached_diff_protocol(self, _mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
protocol = "OTHER"
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'volume_type_id': None,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'status': 'in-use',
'host': HPE3PARBaseDriver.FAKE_HOST,
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
loc_info = 'HPE3PARDriver:1234567:CPG-FC1'
host = {'host': 'stack@3parfc1',
'capabilities': {'location_info': loc_info,
'storage_protocol': protocol}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((False, None), result)
expected = []
mock_client.assert_has_calls(expected)
@ddt.data({'temp_rename_side_effect': None,
'rename_side_effect': None},
{'temp_rename_side_effect': hpeexceptions.HTTPNotFound,
'rename_side_effect': None})
@ddt.unpack
def test_update_migrated_volume(self,
temp_rename_side_effect,
rename_side_effect):
mock_client = self.setup_driver()
mock_client.modifyVolume.side_effect = [temp_rename_side_effect,
rename_side_effect,
None]
fake_old_volume = self.fake_volume_object(self.VOLUME_ID)
provider_location = 'foo'
fake_new_volume = self.fake_volume_object(
self.CLONE_ID, _name_id=self.CLONE_ID,
provider_location=provider_location)
original_volume_status = 'available'
_3common = hpecommon.HPE3PARCommon
self.mock_object(_3common, '_create_client', return_value=mock_client)
mock_get_comment = self.mock_object(_3common, '_get_updated_comment',
side_effect=[mock.sentinel.comm1,
mock.sentinel.comm2])
actual_update = self.driver.update_migrated_volume(
context.get_admin_context(), fake_old_volume,
fake_new_volume, original_volume_status)
if rename_side_effect is None:
expected_update = {'_name_id': None,
'provider_location': None}
else:
expected_update = {'_name_id': fake_new_volume['_name_id'],
'provider_location': provider_location}
self.assertEqual(expected_update, actual_update)
# Initial temp rename always takes place
expected = [
mock.call.modifyVolume(
'osv-0DM4qZEVSKON-DXN-NwVpw',
{'newName': u'tsv-0DM4qZEVSKON-DXN-NwVpw'})
]
comment_expected = []
# Primary rename will occur unless the temp rename fails
if temp_rename_side_effect != hpeexceptions.HTTPConflict:
expected += [
mock.call.modifyVolume(
'osv-0DM4qZEVSKON-AAAAAAAAA',
{'newName': u'osv-0DM4qZEVSKON-DXN-NwVpw',
'comment': mock.sentinel.comm1})
]
comment_expected.append(mock.call('osv-0DM4qZEVSKON-AAAAAAAAA',
volume_id=self.VOLUME_ID,
_name_id=None))
# Final temp rename will occur if both of the previous renames
# succeed.
if (temp_rename_side_effect is None and
rename_side_effect is None):
expected += [
mock.call.modifyVolume(
'tsv-0DM4qZEVSKON-DXN-NwVpw',
{'newName': u'osv-0DM4qZEVSKON-AAAAAAAAA',
'comment': mock.sentinel.comm2})
]
comment_expected.append(mock.call('osv-0DM4qZEVSKON-DXN-NwVpw',
volume_id=self.CLONE_ID,
_name_id=None))
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
mock_get_comment.assert_has_calls(comment_expected)
def test_update_migrated_volume_with_name_id(self):
"""We don't use temp rename mechanism when source uses _name_id."""
mock_client = self.setup_driver()
fake_old_volume = self.fake_volume_object(
self.VOLUME_ID, _name_id=self.SRC_CG_VOLUME_ID)
fake_new_volume = self.fake_volume_object(self.CLONE_ID)
_3common = hpecommon.HPE3PARCommon
self.mock_object(_3common, '_create_client', return_value=mock_client)
mock_get_comment = self.mock_object(_3common, '_get_updated_comment',
side_effect=[mock.sentinel.comm])
actual_update = self.driver.update_migrated_volume(
context.get_admin_context(), fake_old_volume,
fake_new_volume, 'available')
expected_update = {'_name_id': None,
'provider_location': None}
self.assertEqual(expected_update, actual_update)
# # After successfully swapping names we have updated the comments
mock_get_comment.assert_called_once_with('osv-0DM4qZEVSKON-AAAAAAAAA',
volume_id=self.VOLUME_ID,
_name_id=None),
expected = [
mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA',
{'newName': u'osv-0DM4qZEVSKON-DXN-NwVpw',
'comment': mock.sentinel.comm}),
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@ddt.data({'temp_rename_side_effect': hpeexceptions.HTTPConflict,
'rename_side_effect': None},
{'temp_rename_side_effect': None,
'rename_side_effect': hpeexceptions.HTTPConflict},
{'temp_rename_side_effect': hpeexceptions.HTTPNotFound,
'rename_side_effect': hpeexceptions.HTTPConflict})
@ddt.unpack
def test_update_migrated_volume_failed(self,
temp_rename_side_effect,
rename_side_effect):
mock_client = self.setup_driver()
fake_old_volume = {'id': self.VOLUME_ID}
provider_location = 'foo'
fake_new_volume = {'id': self.CLONE_ID,
'_name_id': self.CLONE_ID,
'provider_location': provider_location}
original_volume_status = 'available'
_3common = hpecommon.HPE3PARCommon
self.mock_object(_3common, '_create_client', return_value=mock_client)
mock_get_comment = self.mock_object(_3common, '_get_updated_comment',
side_effect=[mock.sentinel.comm])
mock_update_comment = self.mock_object(_3common, '_update_comment')
mock_client.modifyVolume.side_effect = [
temp_rename_side_effect,
rename_side_effect,
None
]
actual_update = self.driver.update_migrated_volume(
context.get_admin_context(), fake_old_volume, fake_new_volume,
original_volume_status)
expected_update = {'_name_id': self.CLONE_ID,
'provider_location': provider_location}
self.assertEqual(expected_update, actual_update)
# Initial temp rename always takes place
expected = [
mock.call.modifyVolume(
'osv-0DM4qZEVSKON-DXN-NwVpw',
{'newName': u'tsv-0DM4qZEVSKON-DXN-NwVpw'}),
]
# Primary rename will occur unless the temp rename fails
if temp_rename_side_effect != hpeexceptions.HTTPConflict:
expected += [
mock.call.modifyVolume(
'osv-0DM4qZEVSKON-AAAAAAAAA',
{'newName': u'osv-0DM4qZEVSKON-DXN-NwVpw',
'comment': mock.sentinel.comm}),
]
mock_get_comment.assert_called_once_with(
'osv-0DM4qZEVSKON-AAAAAAAAA',
volume_id=self.VOLUME_ID,
_name_id=None)
else:
mock_get_comment.assert_not_called()
mock_update_comment.assert_called_once_with(
'osv-0DM4qZEVSKON-AAAAAAAAA',
volume_id=self.VOLUME_ID,
_name_id=self.CLONE_ID)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_update_migrated_volume_attached(self):
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {
'comment': '{"volume_id": %s, "_name_id": ""}' % self.CLONE_ID}
# Simulate old volume had already been live migrated
fake_old_volume = {'id': self.VOLUME_ID}
provider_location = 'foo'
fake_new_volume = {'id': self.CLONE_ID,
'_name_id': '',
'provider_location': provider_location}
original_volume_status = 'in-use'
_3common = hpecommon.HPE3PARCommon
self.mock_object(_3common, '_create_client', return_value=mock_client)
mock_update = self.mock_object(_3common, '_update_comment')
actual_update = self.driver.update_migrated_volume(
context.get_admin_context(), fake_old_volume,
fake_new_volume, original_volume_status)
expected_update = {'_name_id': fake_new_volume['id'],
'provider_location': provider_location}
self.assertEqual(expected_update, actual_update)
vol_name = _3common._get_3par_vol_name(fake_new_volume)
mock_update.assert_called_once_with(vol_name,
volume_id=fake_old_volume['id'],
_name_id=fake_new_volume['id'])
@ddt.data(('snapshot', 'osv-dh-F5VGRTseuujPjbeRBVg'),
('snapshot_name_id', HPE3PARBaseDriver.VOLUME_NAME_ID_3PAR_NAME))
@ddt.unpack
def test_create_snapshot(self, snapshot_attr, vol_name):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
snapshot = getattr(self, snapshot_attr)
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.create_snapshot(snapshot)
comment = {
"volume_id": snapshot['volume_id'],
"display_name": snapshot['display_name'],
"description": snapshot['display_description'],
"volume_name": snapshot['volume_name'],
}
if snapshot['volume'].get('_name_id'):
comment["_name_id"] = snapshot['volume']['_name_id']
expected = [
mock.call.createSnapshot(
'oss-L4I73ONuTci9Fd4ceij-MQ',
vol_name,
{
'comment': Comment(comment),
'readOnly': True})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@ddt.data(('snapshot', 'osv-dh-F5VGRTseuujPjbeRBVg'),
('snapshot_name_id', HPE3PARBaseDriver.VOLUME_NAME_ID_3PAR_NAME))
@ddt.unpack
def test_revert_to_snapshot(self, snapshot_attr, vol_name):
snapshot = getattr(self, snapshot_attr)
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.isOnlinePhysicalCopy.return_value = False
mock_client.promoteVirtualCopy.return_value = {'taskid': 1}
mock_client.getTask.return_value = {'status': 1}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.revert_to_snapshot(self.ctxt, snapshot['volume'],
snapshot)
expected = [
mock.call.isOnlinePhysicalCopy(vol_name),
mock.call.promoteVirtualCopy('oss-L4I73ONuTci9Fd4ceij-MQ',
optional={}),
mock.call.getTask(1)
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_revert_to_snapshot_replicated_volume(self, _mock_volume_types):
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'volume_type': self.volume_type_replicated}}
mock_client = self.setup_driver()
mock_client.isOnlinePhysicalCopy.return_value = True
mock_client.getStorageSystemInfo.return_value = mock.ANY
mock_client.promoteVirtualCopy.return_value = {'taskid': 1}
mock_client.getTask.return_value = {'status': 1}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.revert_to_snapshot(
self.ctxt,
self.volume_replicated,
self.snapshot)
expected = [
mock.call.stopRemoteCopy('rcg-0DM4qZEVSKON-DXN-N'),
mock.call.isOnlinePhysicalCopy('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.promoteVirtualCopy(
'oss-L4I73ONuTci9Fd4ceij-MQ',
optional={'online': True, 'allowRemoteCopyParent': True}),
mock.call.getTask(1),
mock.call.startRemoteCopy('rcg-0DM4qZEVSKON-DXN-N')
]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_delete_snapshot(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.delete_snapshot(self.snapshot)
expected = [
mock.call.deleteVolume('oss-L4I73ONuTci9Fd4ceij-MQ')]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_delete_snapshot_in_use(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
volume = self.volume_snapshot.copy()
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
self.assertEqual(model_update, {})
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
ex = hpeexceptions.HTTPConflict("In use")
ex._error_code = 32
mock_client.deleteVolume = mock.Mock(side_effect=ex)
# Deleting the snapshot that a volume is dependent on should fail
self.assertRaises(exception.SnapshotIsBusy,
self.driver.delete_snapshot,
self.snapshot)
def test_delete_snapshot_not_found(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.create_snapshot(self.snapshot)
try:
ex = hpeexceptions.HTTPNotFound("not found")
mock_client.deleteVolume = mock.Mock(side_effect=ex)
self.driver.delete_snapshot(self.snapshot)
except Exception:
self.fail("Deleting a snapshot that is missing should act "
"as if it worked.")
def test_create_volume_from_snapshot(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
volume = self.volume_snapshot.copy()
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
self.assertEqual(model_update, {})
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_create_volume_from_snapshot_and_extend(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = {
'getTask.return_value': {
'status': 1},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {}
}
mock_client = self.setup_driver(mock_conf=conf)
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume = self.volume_snapshot.copy()
volume['size'] = self.volume['size'] + 10
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
self.assertIsNone(model_update)
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
volume_name_3par = common._encode_name(volume['id'])
osv_matcher = 'osv-' + volume_name_3par
omv_matcher = 'omv-' + volume_name_3par
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False}),
mock.call.copyVolume(
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
mock.call.getTask(mock.ANY),
mock.call.getVolume(osv_matcher),
mock.call.deleteVolume(osv_matcher),
mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
mock.call.growVolume(osv_matcher, 10 * 1024)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_from_snapshot_and_extend_with_qos(
self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = {
'getTask.return_value': {
'status': 1},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {}
}
mock_client = self.setup_driver(mock_conf=conf)
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'cpg': HPE3PAR_CPG_QOS,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
'tdvv': False,
'volume_type': self.volume_type}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume = self.volume_qos.copy()
volume['size'] = self.volume['size'] + 10
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
self.assertIsNone(model_update)
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
volume_name_3par = common._encode_name(volume['id'])
osv_matcher = 'osv-' + volume_name_3par
omv_matcher = 'omv-' + volume_name_3par
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False}),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.copyVolume(
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
mock.call.getTask(mock.ANY),
mock.call.getVolume(osv_matcher),
mock.call.deleteVolume(osv_matcher),
mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
mock.call.growVolume(osv_matcher, 10 * 1024)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_create_volume_from_snapshot_and_extend_copy_fail(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = {
'getTask.return_value': {
'status': 4,
'failure message': 'out of disk space'},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {}
}
mock_client = self.setup_driver(mock_conf=conf)
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = self.volume_snapshot.copy()
volume['size'] = self.volume['size'] + 10
self.assertRaises(exception.CinderException,
self.driver.create_volume_from_snapshot,
volume, self.snapshot)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_from_snapshot_qos(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'cpg': HPE3PAR_CPG,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
'tdvv': False,
'volume_type': self.volume_type}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
volume = self.volume_qos.copy()
model_update = self.driver.create_volume_from_snapshot(
volume, self.snapshot)
self.assertEqual(model_update, {})
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ', {
'comment': comment,
'readOnly': False})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_from_snapshot_as_child(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
_mock_volume_types.return_value = self.volume_type_hos
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
volume = self.volume_hos.copy()
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
self.assertEqual(model_update, {})
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_from_snapshot_as_base(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = {
'getTask.return_value': {
'status': 1},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {}
}
mock_client = self.setup_driver(mock_conf=conf)
volume_type_hos = copy.deepcopy(self.volume_type_hos)
volume_type_hos['extra_specs']['convert_to_base'] = True
_mock_volume_types.return_value = volume_type_hos
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume = self.volume_hos.copy()
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
self.assertIsNone(model_update)
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
volume_name_3par = common._encode_name(volume['id'])
osv_matcher = 'osv-' + volume_name_3par
omv_matcher = 'omv-' + volume_name_3par
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False}),
mock.call.copyVolume(
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
mock.call.getTask(mock.ANY),
mock.call.getVolume(osv_matcher),
mock.call.deleteVolume(osv_matcher),
mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher})]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_from_snapshot_as_child_and_extend(
self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = {
'getTask.return_value': {
'status': 1},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {}
}
mock_client = self.setup_driver(mock_conf=conf)
_mock_volume_types.return_value = self.volume_type_hos
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume = self.volume_hos.copy()
volume['size'] = self.volume['size'] + 10
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
self.assertIsNone(model_update)
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
volume_name_3par = common._encode_name(volume['id'])
osv_matcher = 'osv-' + volume_name_3par
omv_matcher = 'omv-' + volume_name_3par
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False}),
mock.call.copyVolume(
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
mock.call.getTask(mock.ANY),
mock.call.getVolume(osv_matcher),
mock.call.deleteVolume(osv_matcher),
mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
mock.call.growVolume(osv_matcher, 10 * 1024)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_from_snapshot_as_base_and_extend(
self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = {
'getTask.return_value': {
'status': 1},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {}
}
mock_client = self.setup_driver(mock_conf=conf)
volume_type_hos = copy.deepcopy(self.volume_type_hos)
volume_type_hos['extra_specs']['convert_to_base'] = True
_mock_volume_types.return_value = volume_type_hos
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume = self.volume_hos.copy()
volume['size'] = self.volume['size'] + 10
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
self.assertIsNone(model_update)
comment = Comment({
"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",
"display_name": "Foo Volume",
"volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7",
})
volume_name_3par = common._encode_name(volume['id'])
osv_matcher = 'osv-' + volume_name_3par
omv_matcher = 'omv-' + volume_name_3par
expected = [
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False}),
mock.call.copyVolume(
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
mock.call.getTask(mock.ANY),
mock.call.getVolume(osv_matcher),
mock.call.deleteVolume(osv_matcher),
mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
mock.call.growVolume(osv_matcher, 10 * 1024)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_terminate_connection_from_primary_when_failed_over(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getHostVLUNs.side_effect = hpeexceptions.HTTPNotFound(
error={'desc': 'The host does not exist.'})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._active_backend_id = 'some_id'
self.driver.terminate_connection(
self.volume,
self.connector,
force=True)
# When the volume is still attached to the primary array after a
# fail-over, there should be no call to delete the VLUN(s) or the
# host. We can assert these methods were not called to make sure
# the proper exceptions are being raised.
self.assertEqual(0, mock_client.deleteVLUN.call_count)
def test_terminate_connection_from_primary_when_group_failed_over(self):
mock_conf = {
'getStorageSystemInfo.return_value': {
'id': self.REPLICATION_CLIENT_ID,
'name': 'CSIM-EOS12_1611702'}}
conf = self.setup_configuration()
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf, mock_conf=mock_conf)
mock_client.getHostVLUNs.side_effect = hpeexceptions.HTTPNotFound(
error={'desc': 'The host does not exist.'})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = self.volume_tiramisu.copy()
volume['replication_status'] = 'failed-over'
volume['replication_driver_data'] = self.REPLICATION_CLIENT_ID
self.driver._active_backend_id = "CSIM-EOS12_1611702"
self.driver.terminate_connection(
self.volume,
self.connector,
force=True)
# When the volume is still attached to the primary array after a
# fail-over, there should be no call to delete the VLUN(s) or the
# host. We can assert these methods were not called to make sure
# the proper exceptions are being raised.
self.assertEqual(0, mock_client.deleteVLUN.call_count)
def test_extend_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
grow_size = 3
old_size = self.volume['size']
new_size = old_size + grow_size
self.driver.extend_volume(self.volume, str(new_size))
growth_size_mib = grow_size * units.Ki
expected = [
mock.call.growVolume(self.VOLUME_3PAR_NAME, growth_size_mib)]
mock_client.assert_has_calls(expected)
def test_extend_volume_non_base(self):
extend_ex = hpeexceptions.HTTPForbidden(error={'code': 150})
conf = {
'getTask.return_value': {
'status': 1},
'getCPG.return_value': {},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {},
# Throw an exception first time only
'growVolume.side_effect': [extend_ex,
None],
}
mock_client = self.setup_driver(mock_conf=conf)
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
grow_size = 3
old_size = self.volume['size']
new_size = old_size + grow_size
self.driver.extend_volume(self.volume, str(new_size))
self.assertEqual(2, mock_client.growVolume.call_count)
def test_extend_volume_non_base_failure(self):
extend_ex = hpeexceptions.HTTPForbidden(error={'code': 150})
conf = {
'getTask.return_value': {
'status': 1},
'getCPG.return_value': {},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {},
# Always fail
'growVolume.side_effect': extend_ex
}
mock_client = self.setup_driver(mock_conf=conf)
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
grow_size = 3
old_size = self.volume['size']
new_size = old_size + grow_size
self.assertRaises(hpeexceptions.HTTPForbidden,
self.driver.extend_volume,
self.volume,
str(new_size))
@mock.patch.object(volume_types, 'get_volume_type')
def test_extend_volume_replicated(self, _mock_volume_types):
# Managed vs. unmanaged and periodic vs. sync are not relevant when
# extending a replicated volume type.
# We will use managed and periodic as the default.
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'cpg': HPE3PAR_CPG,
'snap_cpg': HPE3PAR_CPG_SNAP,
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
grow_size = 3
old_size = self.volume_replicated['size']
new_size = old_size + grow_size
# Test a successful extend.
self.driver.extend_volume(
self.volume_replicated,
new_size)
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_NAME),
mock.call.growVolume(self.VOLUME_3PAR_NAME, grow_size * 1024),
mock.call.startRemoteCopy(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
# Test an unsuccessful extend. growVolume will fail but remote
# copy should still be started again.
mock_client.growVolume.side_effect = (
hpeexceptions.HTTPForbidden("Error: The volume cannot be "
"extended."))
self.assertRaises(
hpeexceptions.HTTPForbidden,
self.driver.extend_volume,
self.volume_replicated,
new_size)
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_NAME),
mock.call.growVolume(self.VOLUME_3PAR_NAME, grow_size * 1024),
mock.call.startRemoteCopy(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_get_ports(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getPorts.return_value = {
'members': [
{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.120.252',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8},
{'portWWN': '20210002AC00383D',
'protocol': 1,
'linkState': 4,
'mode': 2,
'device': ['cage2'],
'nodeWWN': '20210002AC00383D',
'type': 2,
'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
ports = common.get_ports()['members']
self.assertEqual(3, len(ports))
def test_get_by_qos_spec_with_scoping(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS)
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:maxIOPS": "100",
"qos:maxBWS": "50",
"qos:minIOPS": "10",
"qos:minBWS": "20",
"qos:latency": "5",
"qos:priority": "high"})
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
qos = common._get_qos_by_volume_type(type_ref)
self.assertEqual({'maxIOPS': '1000', 'maxBWS': '50',
'minIOPS': '100', 'minBWS': '25',
'latency': '25', 'priority': 'low'}, qos)
def test_get_by_qos_spec(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
qos_ref = qos_specs.create(
self.ctxt,
'qos-specs-1',
self.QOS_SPECS)
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:maxIOPS": "100",
"qos:maxBWS": "50",
"qos:minIOPS": "10",
"qos:minBWS": "20",
"qos:latency": "5",
"qos:priority": "high"})
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
qos = common._get_qos_by_volume_type(type_ref)
self.assertEqual({'maxIOPS': '1000', 'maxBWS': '50',
'minIOPS': '100', 'minBWS': '25',
'latency': '25', 'priority': 'low'}, qos)
def test_get_by_qos_by_type_only(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:maxIOPS": "100",
"qos:maxBWS": "50",
"qos:minIOPS": "10",
"qos:minBWS": "20",
"qos:latency": "5",
"qos:priority": "high"})
type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
qos = common._get_qos_by_volume_type(type_ref)
self.assertEqual({'maxIOPS': '100', 'maxBWS': '50',
'minIOPS': '10', 'minBWS': '20',
'latency': '5', 'priority': 'high'}, qos)
def test_create_vlun(self):
host = 'fake-host'
lun_id = 11
nsp = '1:2:3'
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
location = ("%(name)s,%(lunid)s,%(host)s,%(nsp)s" %
{'name': self.VOLUME_NAME,
'lunid': lun_id,
'host': host,
'nsp': nsp})
mock_client.createVLUN.return_value = location
expected_info = {'volume_name': self.VOLUME_NAME,
'lun_id': lun_id,
'host_name': host,
'nsp': nsp}
common = self.driver._login()
vlun_info = common._create_3par_vlun(
self.VOLUME_NAME,
host,
nsp)
self.assertEqual(expected_info, vlun_info)
location = ("%(name)s,%(lunid)s,%(host)s" %
{'name': self.VOLUME_NAME,
'lunid': lun_id,
'host': host})
mock_client.createVLUN.return_value = location
expected_info = {'volume_name': self.VOLUME_NAME,
'lun_id': lun_id,
'host_name': host}
vlun_info = common._create_3par_vlun(
self.VOLUME_NAME,
host,
None)
self.assertEqual(expected_info, vlun_info)
def test_create_vlun_vlunid_zero(self):
# This will test "auto" for deactive when Lun ID is 0
host = 'fake-host'
lun_id = 0
nsp = '0:1:1'
port = {'node': 0, 'slot': 1, 'cardPort': 1}
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
# _create_3par_vlun with nsp
location = ("%(name)s,%(lunid)s,%(host)s,%(nsp)s" %
{'name': self.VOLUME_NAME,
'lunid': lun_id,
'host': host,
'nsp': nsp})
mock_client.createVLUN.return_value = location
expected_info = {'volume_name': self.VOLUME_NAME,
'lun_id': lun_id,
'host_name': host,
'nsp': nsp}
common = self.driver._login()
vlun_info = common._create_3par_vlun(
self.VOLUME_NAME,
host,
nsp,
lun_id=lun_id)
self.assertEqual(expected_info, vlun_info)
mock_client.createVLUN.assert_called_once_with(self.VOLUME_NAME,
hostname=host,
auto=False,
portPos=port,
lun=lun_id)
# _create_3par_vlun without nsp
mock_client.reset_mock()
location = ("%(name)s,%(lunid)s,%(host)s" %
{'name': self.VOLUME_NAME,
'lunid': lun_id,
'host': host})
mock_client.createVLUN.return_value = location
expected_info = {'volume_name': self.VOLUME_NAME,
'lun_id': lun_id,
'host_name': host}
vlun_info = common._create_3par_vlun(
self.VOLUME_NAME,
host,
None,
lun_id=lun_id)
self.assertEqual(expected_info, vlun_info)
mock_client.createVLUN.assert_called_once_with(self.VOLUME_NAME,
hostname=host,
auto=False,
lun=lun_id)
def test__get_existing_volume_ref_name(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
ums_matcher = common._get_3par_ums_name(self.volume['id'])
existing_ref = {'source-name': unm_matcher}
result = common._get_existing_volume_ref_name(existing_ref)
self.assertEqual(unm_matcher, result)
existing_ref = {'source-id': self.volume['id']}
result = common._get_existing_volume_ref_name(existing_ref)
self.assertEqual(unm_matcher, result)
existing_ref = {'source-id': self.volume['id']}
result = common._get_existing_volume_ref_name(existing_ref, True)
self.assertEqual(ums_matcher, result)
existing_ref = {'bad-key': 'foo'}
self.assertRaises(
exception.ManageExistingInvalidReference,
common._get_existing_volume_ref_name,
existing_ref)
@mock.patch.object(volume_types, 'get_volume_type')
def test_manage_existing(self, _mock_volume_types):
_mock_volume_types.return_value = self.volume_type
mock_client = self.setup_driver()
new_comment = Comment({
"display_name": "Foo Volume",
"name": "volume-007dbfce-7579-40bc-8f90-a20b3902283e",
"volume_id": "007dbfce-7579-40bc-8f90-a20b3902283e",
"type": "OpenStack",
})
volume = {'display_name': None,
'host': self.FAKE_CINDER_HOST,
'volume_type': 'gold',
'volume_type_id': 'acfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}
mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
osv_matcher = common._get_3par_vol_name(volume['id'])
vvs_matcher = common._get_3par_vvs_name(volume['id'])
existing_ref = {'source-name': unm_matcher}
expected_obj = {'display_name': 'Foo Volume'}
obj = self.driver.manage_existing(volume, existing_ref)
expected_manage = [
mock.call.getVolume(existing_ref['source-name']),
mock.call.modifyVolume(existing_ref['source-name'],
{'newName': osv_matcher,
'comment': new_comment}),
]
retype_comment_qos = Comment({
"display_name": "Foo Volume",
"volume_type_name": self.volume_type['name'],
"volume_type_id": self.volume_type['id'],
"qos": {
'maxIOPS': '1000',
'maxBWS': '50',
'minIOPS': '100',
'minBWS': '25',
'latency': '25',
'priority': 'low'
}
})
expected_snap_cpg = HPE3PAR_CPG_SNAP
expected_retype_modify = [
mock.call.modifyVolume(osv_matcher,
{'comment': retype_comment_qos,
'snapCPG': expected_snap_cpg}),
mock.call.deleteVolumeSet(vvs_matcher),
]
expected_retype_specs = [
mock.call.createVolumeSet(vvs_matcher, None),
mock.call.createQoSRules(
vvs_matcher,
{'ioMinGoal': 100, 'ioMaxLimit': 1000,
'bwMinGoalKB': 25600, 'priority': 1, 'latencyGoal': 25,
'bwMaxLimitKB': 51200}),
mock.call.addVolumeToVolumeSet(vvs_matcher, osv_matcher),
mock.call.modifyVolume(
osv_matcher,
{'action': 6,
'userCPG': HPE3PAR_CPG,
'conversionOperation': 1, 'tuneOperation': 1,
'compression': False}),
mock.call.getTask(1)
]
mock_client.assert_has_calls(self.standard_login + expected_manage)
mock_client.assert_has_calls(expected_retype_modify)
mock_client.assert_has_calls(
expected_retype_specs +
self.standard_logout)
self.assertEqual(expected_obj, obj)
@mock.patch.object(volume_types, 'get_volume_type')
def test_manage_existing_with_no_snap_cpg(self, _mock_volume_types):
_mock_volume_types.return_value = self.volume_type
mock_client = self.setup_driver()
new_comment = Comment({
"display_name": "Foo Volume",
"name": "volume-007dbfce-7579-40bc-8f90-a20b3902283e",
"volume_id": "007dbfce-7579-40bc-8f90-a20b3902283e",
"type": "OpenStack",
})
volume = {'display_name': None,
'host': 'my-stack1@3parxxx#CPGNOTUSED',
'volume_type': 'gold',
'volume_type_id': 'acfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}
mock_client.getVolume.return_value = self.MV_INFO_WITH_NO_SNAPCPG
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
osv_matcher = common._get_3par_vol_name(volume['id'])
existing_ref = {'source-name': unm_matcher}
expected_obj = {'display_name': 'Foo Volume'}
obj = self.driver.manage_existing(volume, existing_ref)
expected_manage = [
mock.call.getVolume(existing_ref['source-name']),
mock.call.modifyVolume(
existing_ref['source-name'],
{'newName': osv_matcher,
'comment': new_comment,
# manage_existing() should be setting
# blank snapCPG to the userCPG
'snapCPG': 'testUserCpg0'})
]
mock_client.assert_has_calls(self.standard_login + expected_manage)
self.assertEqual(expected_obj, obj)
@mock.patch.object(volume_types, 'get_volume_type')
def test_manage_existing_vvs(self, _mock_volume_types):
test_volume_type = self.RETYPE_VOLUME_TYPE_2
vvs = test_volume_type['extra_specs']['vvs']
_mock_volume_types.return_value = test_volume_type
mock_client = self.setup_driver()
mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
id = '007abcde-7579-40bc-8f90-a20b3902283e'
new_comment = Comment({
"display_name": "Test Volume",
"name": ("volume-%s" % id),
"volume_id": id,
"type": "OpenStack",
})
volume = {'display_name': 'Test Volume',
'host': 'my-stack1@3parxxx#CPGNOTUSED',
'volume_type': 'gold',
'volume_type_id': 'acfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
'id': id}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
osv_matcher = common._get_3par_vol_name(volume['id'])
vvs_matcher = common._get_3par_vvs_name(volume['id'])
existing_ref = {'source-name': unm_matcher}
obj = self.driver.manage_existing(volume, existing_ref)
expected_obj = {'display_name': 'Test Volume'}
expected_manage = [
mock.call.getVolume(existing_ref['source-name']),
mock.call.modifyVolume(existing_ref['source-name'],
{'newName': osv_matcher,
'comment': new_comment})
]
retype_comment_vvs = Comment({
"display_name": "Foo Volume",
"volume_type_name": test_volume_type['name'],
"volume_type_id": test_volume_type['id'],
"vvs": vvs
})
expected_retype = [
mock.call.modifyVolume(osv_matcher,
{'comment': retype_comment_vvs,
'snapCPG': 'OpenStackCPGSnap'}),
mock.call.deleteVolumeSet(vvs_matcher),
mock.call.addVolumeToVolumeSet(vvs, osv_matcher),
mock.call.modifyVolume(osv_matcher,
{'action': 6,
'userCPG': 'CPGNOTUSED',
'conversionOperation': 1,
'tuneOperation': 1,
'compression': False}),
mock.call.getTask(1)
]
mock_client.assert_has_calls(self.standard_login + expected_manage)
mock_client.assert_has_calls(
expected_retype +
self.standard_logout)
self.assertEqual(expected_obj, obj)
def test_manage_existing_no_volume_type(self):
mock_client = self.setup_driver()
comment = repr({"display_name": "Foo Volume"})
new_comment = Comment({
"type": "OpenStack",
"display_name": "Foo Volume",
"name": "volume-007dbfce-7579-40bc-8f90-a20b3902283e",
"volume_id": "007dbfce-7579-40bc-8f90-a20b3902283e",
})
volume = {'display_name': None,
'volume_type': None,
'volume_type_id': None,
'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}
mock_client.getVolume.return_value = {'comment': comment,
'userCPG': 'testUserCpg0'}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
osv_matcher = common._get_3par_vol_name(volume['id'])
existing_ref = {'source-name': unm_matcher}
obj = self.driver.manage_existing(volume, existing_ref)
expected_obj = {'display_name': 'Foo Volume'}
expected = [
mock.call.getVolume(existing_ref['source-name']),
mock.call.modifyVolume(existing_ref['source-name'],
{'newName': osv_matcher,
'comment': new_comment,
# manage_existing() should be setting
# blank snapCPG to the userCPG
'snapCPG': 'testUserCpg0'})
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_obj, obj)
volume['display_name'] = 'Test Volume'
obj = self.driver.manage_existing(volume, existing_ref)
expected_obj = {'display_name': 'Test Volume'}
expected = [
mock.call.getVolume(existing_ref['source-name']),
mock.call.modifyVolume(existing_ref['source-name'],
{'newName': osv_matcher,
'comment': new_comment,
# manage_existing() should be setting
# blank snapCPG to the userCPG
'snapCPG': 'testUserCpg0'})
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_obj, obj)
mock_client.getVolume.return_value = {'userCPG': 'testUserCpg0'}
volume['display_name'] = None
common = self.driver._login()
obj = self.driver.manage_existing(volume, existing_ref)
expected_obj = {'display_name': None}
expected = [
mock.call.getVolume(existing_ref['source-name']),
mock.call.modifyVolume(existing_ref['source-name'],
{'newName': osv_matcher,
'comment': new_comment,
# manage_existing() should be setting
# blank snapCPG to the userCPG
'snapCPG': 'testUserCpg0'})
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_obj, obj)
def test_manage_existing_invalid_input(self):
mock_client = self.setup_driver()
volume = {'display_name': None,
'volume_type': None,
'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}
mock_client.getVolume.side_effect = hpeexceptions.HTTPNotFound('fake')
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
existing_ref = {'source-name': unm_matcher}
self.assertRaises(exception.InvalidInput,
self.driver.manage_existing,
volume=volume,
existing_ref=existing_ref)
expected = [mock.call.getVolume(existing_ref['source-name'])]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_manage_existing_volume_type_exception(self):
mock_client = self.setup_driver()
comment = repr({"display_name": "Foo Volume"})
volume = {'display_name': None,
'volume_type': 'gold',
'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}
mock_client.getVolume.return_value = {'comment': comment}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
existing_ref = {'source-name': unm_matcher}
self.assertRaises(exception.ManageExistingVolumeTypeMismatch,
self.driver.manage_existing,
volume=volume,
existing_ref=existing_ref)
expected = [mock.call.getVolume(existing_ref['source-name'])]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_manage_existing_retype_exception(self, _mock_volume_types):
mock_client = self.setup_driver()
_mock_volume_types.return_value = {
'name': 'gold',
'id': 'gold-id',
'extra_specs': {
'cpg': HPE3PAR_CPG,
'snap_cpg': HPE3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
'tdvv': False,
'volume_type': self.volume_type}}
volume = {'display_name': None,
'host': 'stack1@3pariscsi#POOL1',
'volume_type': 'gold',
'volume_type_id': 'bcfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}
mock_client.getVolume.return_value = self.MANAGE_VOLUME_INFO
mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1})
mock_client.getTask.return_value = self.STATUS_DONE
mock_client.getCPG.side_effect = [
{'domain': 'domain1'},
{'domain': 'domain2'},
{'domain': 'domain3'},
]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
osv_matcher = common._get_3par_vol_name(volume['id'])
existing_ref = {'source-name': unm_matcher}
self.assertRaises(hpecommon.Invalid3PARDomain,
self.driver.manage_existing,
volume=volume,
existing_ref=existing_ref)
expected = [
mock.call.getVolume(unm_matcher),
mock.call.modifyVolume(
unm_matcher, {
'newName': osv_matcher,
'comment': mock.ANY}),
mock.call.getCPG('POOL1'),
mock.call.getVolume(osv_matcher),
mock.call.getCPG('testUserCpg0'),
mock.call.getCPG('POOL1'),
mock.call.modifyVolume(
osv_matcher, {'newName': unm_matcher,
'comment': self.MANAGE_VOLUME_INFO
['comment']})
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_manage_existing_snapshot(self):
mock_client = self.setup_driver()
new_comment = Comment({
"display_name": "snap",
"volume_name": self.VOLUME_NAME,
"volume_id": self.VOLUME_ID,
"description": "",
})
volume = {'id': self.VOLUME_ID}
snapshot = {
'display_name': None,
'id': self.SNAPSHOT_ID,
'volume': volume,
}
mock_client.getVolume.return_value = {
"comment": "{'display_name': 'snap'}",
'copyOf': self.VOLUME_NAME_3PAR,
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
oss_matcher = common._get_3par_snap_name(snapshot['id'])
ums_matcher = common._get_3par_ums_name(snapshot['id'])
existing_ref = {'source-name': ums_matcher}
expected_obj = {'display_name': 'snap'}
obj = self.driver.manage_existing_snapshot(snapshot, existing_ref)
expected = [
mock.call.getVolume(existing_ref['source-name']),
mock.call.modifyVolume(existing_ref['source-name'],
{'newName': oss_matcher,
'comment': new_comment}),
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_obj, obj)
def test_manage_existing_snapshot_invalid_parent(self):
mock_client = self.setup_driver()
volume = {'id': self.VOLUME_ID}
snapshot = {
'display_name': None,
'id': '007dbfce-7579-40bc-8f90-a20b3902283e',
'volume': volume,
}
mock_client.getVolume.return_value = {
"comment": "{'display_name': 'snap'}",
'copyOf': 'fake-invalid',
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
ums_matcher = common._get_3par_ums_name(snapshot['id'])
existing_ref = {'source-name': ums_matcher}
self.assertRaises(exception.InvalidInput,
self.driver.manage_existing_snapshot,
snapshot=snapshot,
existing_ref=existing_ref)
expected = [
mock.call.getVolume(existing_ref['source-name']),
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_manage_existing_snapshot_failed_over_volume(self):
mock_client = self.setup_driver()
volume = {
'id': self.VOLUME_ID,
'replication_status': 'failed-over',
}
snapshot = {
'display_name': None,
'id': '007dbfce-7579-40bc-8f90-a20b3902283e',
'volume': volume,
}
mock_client.getVolume.return_value = {
"comment": "{'display_name': 'snap'}",
'copyOf': self.VOLUME_NAME_3PAR,
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
ums_matcher = common._get_3par_ums_name(snapshot['id'])
existing_ref = {'source-name': ums_matcher}
self.assertRaises(exception.InvalidInput,
self.driver.manage_existing_snapshot,
snapshot=snapshot,
existing_ref=existing_ref)
def test_manage_existing_get_size(self):
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'sizeMiB': 2048}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
volume = {}
existing_ref = {'source-name': unm_matcher}
size = self.driver.manage_existing_get_size(volume, existing_ref)
expected_size = 2
expected = [mock.call.getVolume(existing_ref['source-name'])]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_size, size)
def test_manage_existing_get_size_invalid_reference(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = {}
existing_ref = {'source-name': self.VOLUME_3PAR_NAME}
self.assertRaises(exception.ManageExistingInvalidReference,
self.driver.manage_existing_get_size,
volume=volume,
existing_ref=existing_ref)
mock_client.assert_has_calls(
self.standard_login +
self.standard_logout)
existing_ref = {}
self.assertRaises(exception.ManageExistingInvalidReference,
self.driver.manage_existing_get_size,
volume=volume,
existing_ref=existing_ref)
mock_client.assert_has_calls(
self.standard_login +
self.standard_logout)
def test_manage_existing_get_size_invalid_input(self):
mock_client = self.setup_driver()
mock_client.getVolume.side_effect = hpeexceptions.HTTPNotFound('fake')
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
unm_matcher = common._get_3par_unm_name(self.volume['id'])
volume = {}
existing_ref = {'source-name': unm_matcher}
self.assertRaises(exception.InvalidInput,
self.driver.manage_existing_get_size,
volume=volume,
existing_ref=existing_ref)
expected = [mock.call.getVolume(existing_ref['source-name'])]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_manage_existing_snapshot_get_size(self):
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'sizeMiB': 2048}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
ums_matcher = common._get_3par_ums_name(self.snapshot['id'])
snapshot = {}
existing_ref = {'source-name': ums_matcher}
size = self.driver.manage_existing_snapshot_get_size(snapshot,
existing_ref)
expected_size = 2
expected = [mock.call.getVolume(existing_ref['source-name'])]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_size, size)
def test_manage_existing_snapshot_get_size_invalid_reference(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
snapshot = {}
existing_ref = {'source-name': self.SNAPSHOT_3PAR_NAME}
self.assertRaises(exception.ManageExistingInvalidReference,
self.driver.manage_existing_snapshot_get_size,
snapshot=snapshot,
existing_ref=existing_ref)
mock_client.assert_has_calls(
self.standard_login +
self.standard_logout)
existing_ref = {}
self.assertRaises(exception.ManageExistingInvalidReference,
self.driver.manage_existing_snapshot_get_size,
snapshot=snapshot,
existing_ref=existing_ref)
mock_client.assert_has_calls(
self.standard_login +
self.standard_logout)
def test_manage_existing_snapshot_get_size_invalid_input(self):
mock_client = self.setup_driver()
mock_client.getVolume.side_effect = hpeexceptions.HTTPNotFound('fake')
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
ums_matcher = common._get_3par_ums_name(self.snapshot['id'])
snapshot = {}
existing_ref = {'source-name': ums_matcher}
self.assertRaises(exception.InvalidInput,
self.driver.manage_existing_snapshot_get_size,
snapshot=snapshot,
existing_ref=existing_ref)
expected = [mock.call.getVolume(existing_ref['source-name'])]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_unmanage(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.driver.unmanage(self.volume)
osv_matcher = common._get_3par_vol_name(self.volume['id'])
unm_matcher = common._get_3par_unm_name(self.volume['id'])
expected = [
mock.call.modifyVolume(osv_matcher, {'newName': unm_matcher})
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_unmanage_snapshot(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.driver.unmanage_snapshot(self.snapshot)
oss_matcher = common._get_3par_snap_name(self.snapshot['id'])
ums_matcher = common._get_3par_ums_name(self.snapshot['id'])
expected = [
mock.call.modifyVolume(oss_matcher, {'newName': ums_matcher})
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_unmanage_snapshot_failed_over_volume(self):
mock_client = self.setup_driver()
volume = {'replication_status': 'failed-over', }
snapshot = {'id': self.SNAPSHOT_ID,
'display_name': 'fake_snap',
'volume': volume, }
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(exception.SnapshotIsBusy,
self.driver.unmanage_snapshot,
snapshot=snapshot)
@ddt.data(True, False)
def test__safe_hostname(self, in_shared):
config = self._set_unique_fqdn_override(True, in_shared)
my_connector = self.connector.copy()
my_connector['host'] = "abc123abc123abc123abc123abc123abc123"
fixed_hostname = "abc123abc123abc123abc123abc123a"
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
safe_host = common._safe_hostname(my_connector, config)
self.assertEqual(fixed_hostname, safe_host)
@ddt.data(True, False)
def test__safe_hostname_unique(self, in_shared):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
config = self._set_unique_fqdn_override(False, in_shared)
my_connector = self.connector.copy()
my_connector['host'] = "abc123abc123abc123abc123abc123abc123"
my_connector['initiator'] = 'iqn.1993-08.org.debian:01:222:12345'
ret_name = '54321-222-10-naibed.gro.80-3991'
safe_host = common._safe_hostname(my_connector, config)
self.assertEqual(ret_name, safe_host)
@ddt.data(True, False)
def test__safe_hostname_unique_without_initiator(self, in_shared):
fixed_hostname = "abc123abc123abc123abc123abc123a"
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
conf = self._set_unique_fqdn_override(False, in_shared)
my_connector = self.connector.copy()
del(my_connector['initiator'])
my_connector['host'] = "abc123abc123abc123abc123abc123abc123"
safe_host = common._safe_hostname(my_connector, conf)
self.assertEqual(fixed_hostname, safe_host)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
comment = Comment({
'group_id': self.GROUP_ID
})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
# create a group
group = self.fake_group_object()
self.driver.create_group(context.get_admin_context(), group)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=comment)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_with_replication_enabled(self, cg_ss_enable,
vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
comment = Comment({
'group_id': self.GROUP_ID
})
mock_client.getRemoteCopyGroup.return_value = (
{'volumes': []})
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
mock_client.getCPG.return_value = {'domain': None}
# create a group
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
backend_id = self.replication_targets[0]['backend_id']
exp_model_update = {'status': fields.GroupStatus.AVAILABLE,
'replication_status':
fields.ReplicationStatus.ENABLED}
model_update = \
self.driver.create_group(context.get_admin_context(), group)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createRemoteCopyGroup(
self.RCG_3PAR_GROUP_NAME,
[{'targetName': backend_id,
'mode': SYNC_MODE}], {}),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=comment)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(exp_model_update, model_update)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_empty_group_with_replication_enabled(self, cg_ss_enable,
vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
mock_client.getRemoteCopyGroup.return_value = (
{'volumes': []})
# create a consistency group
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
group.status = fields.GroupStatus.DELETING
self.driver.delete_group(context.get_admin_context(), group, [])
expected = [
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.removeRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.deleteVolumeSet(
self.CONSIS_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_group_with_replication_enabled(self, cg_ss_enable,
vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
exp_volume_model_updates = [{'id': self.volume['id'],
'status': 'deleted'}]
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
mock_client.getRemoteCopyGroup.return_value = (
{'volumes': [{'name': self.VOLUME_3PAR_NAME}]})
# create a consistency group
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
group.status = fields.GroupStatus.DELETING
model_update, volume_model_updates = (
self.driver.delete_group(context.get_admin_context(),
group, [self.volume]))
expected = [
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.removeVolumeFromRemoteCopyGroup(
self.RCG_3PAR_GROUP_NAME, self.VOLUME_3PAR_NAME,
removeFromTarget=True),
mock.call.removeRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.deleteVolumeSet(
self.CONSIS_GROUP_NAME),
mock.call.deleteVolume(self.VOLUME_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(exp_volume_model_updates, volume_model_updates)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_enable_group_replication(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getVolumeSet.return_value = (
{'name': self.CONSIS_GROUP_NAME})
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
self.driver.enable_replication(context.get_admin_context(),
group, [self.volume])
expected = [
mock.call.getVolumeSet(self.CONSIS_GROUP_NAME),
mock.call.startRemoteCopy(self.RCG_3PAR_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_disable_group_replication(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getVolumeSet.return_value = (
{'name': self.CONSIS_GROUP_NAME})
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
self.driver.disable_replication(context.get_admin_context(),
group, [self.volume])
expected = [
mock.call.getVolumeSet(self.CONSIS_GROUP_NAME),
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_failover_replication_on_group(self, _mock_volume_types):
# periodic vs. sync is not relevant when conducting a failover. We
# will just use periodic.
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
valid_backend_id = (
self.replication_targets[0]['backend_id'])
# create a group
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
volumes = [self.volume_tiramisu]
expected_model = [{'id': self.VOLUME_ID,
'replication_status':
fields.ReplicationStatus.FAILED_OVER,
'provider_location': self.CLIENT_ID,
'replication_driver_data':
self.REPLICATION_CLIENT_ID}]
exp_model_update = {
'replication_status': fields.ReplicationStatus.FAILED_OVER}
model_update, return_model = self.driver.failover_replication(
context.get_admin_context(),
group,
volumes,
valid_backend_id)
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_model, return_model)
self.assertEqual(exp_model_update, model_update)
@mock.patch.object(volume_types, 'get_volume_type')
def test_failback_replication_on_group(self, _mock_volume_types):
# Managed vs. unmanaged and periodic vs. sync are not relevant when
# failing back a volume.
# We will use managed and periodic as the default.
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
volume = self.volume_tiramisu.copy()
volume['replication_status'] = 'failed-over'
volume['replication_driver_data'] = self.REPLICATION_CLIENT_ID
# create a group
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
expected_model = [{'id': self.VOLUME_ID,
'replication_status':
fields.ReplicationStatus.ENABLED,
'provider_location': self.CLIENT_ID,
'replication_driver_data':
self.REPLICATION_CLIENT_ID}]
exp_model_update = {
'replication_status': fields.ReplicationStatus.ENABLED}
model_update, return_model = self.driver.failover_replication(
context.get_admin_context(),
group,
[volume],
'default')
self.assertEqual(expected_model, return_model)
self.assertEqual(exp_model_update, model_update)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type')
def test_update_replication_enabled_group_add_vol(self, _mock_volume_types,
cg_ss_enable,
vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
mock_client.getCPG.return_value = {'domain': None}
mock_client.getRemoteCopyGroup.return_value = (
{'volumes': [{'name': self.VOLUME_3PAR_NAME}]})
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
# create a group
group = self.fake_group_object()
group.is_replicated = True
group.replication_status = fields.ReplicationStatus.ENABLED
group.volume_types = [self.volume_type_tiramisu]
exp_add_volume = [{'id': self.volume_tiramisu['id'],
'replication_status':
fields.ReplicationStatus.ENABLED}]
# add a volume to the consistency group
model_update, add_volume, remove_volume = \
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[self.volume_tiramisu],
remove_volumes=[])
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_GROUP_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': 'target'}],
optional={'volumeAutoCreation': True}),
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_3PAR_NAME),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.startRemoteCopy(self.RCG_3PAR_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(exp_add_volume, add_volume)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type')
def test_update_repl_group_add_periodic_vol(self, _mock_volume_types,
cg_ss_enable,
vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': 300,
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
mock_client.getCPG.return_value = {'domain': None}
mock_client.getRemoteCopyGroup.return_value = (
{'volumes': [{'name': self.VOLUME_3PAR_NAME}],
'targets': [{'syncPeriod': 0}]})
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
# create a group
group = self.fake_group_object()
group.is_replicated = True
group.replication_status = fields.ReplicationStatus.ENABLED
group.volume_types = [self.volume_type_tiramisu]
exp_add_volume = [{'id': self.volume_tiramisu['id'],
'replication_status':
fields.ReplicationStatus.ENABLED}]
# add a volume to the consistency group
model_update, add_volume, remove_volume = \
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[self.volume_tiramisu],
remove_volumes=[])
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_GROUP_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': 'target'}],
optional={'volumeAutoCreation': True}),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.modifyRemoteCopyGroup(
self.RCG_3PAR_GROUP_NAME,
{'targets': [
{'syncPeriod': 300, 'targetName': 'target'}]}),
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_3PAR_NAME),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.startRemoteCopy(self.RCG_3PAR_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(exp_add_volume, add_volume)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type')
def test_update_replication_enabled_group_remove_vol(
self, _mock_volume_types, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
mock_client.getCPG.return_value = {'domain': None}
mock_client.getRemoteCopyGroup.return_value = (
{'volumes': [{'name': self.VOLUME_3PAR_NAME}]})
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
# create a group
group = self.fake_group_object()
group.is_replicated = True
group.replication_status = fields.ReplicationStatus.ENABLED
group.volume_types = [self.volume_type_tiramisu]
exp_remove_volume = [{'id': self.volume_tiramisu['id'],
'replication_status': None}]
# add a volume to the consistency group
model_update, add_volume, remove_volume = \
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[],
remove_volumes=[self.volume_tiramisu])
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.removeVolumeFromRemoteCopyGroup(
self.RCG_3PAR_GROUP_NAME, self.VOLUME_3PAR_NAME,
removeFromTarget=True),
mock.call.removeVolumeFromVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_3PAR_NAME),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.startRemoteCopy(self.RCG_3PAR_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(exp_remove_volume, remove_volume)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_with_tiramisu_volume_type(self, _mock_volume_types,
vol_ss_enable):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getCPG.return_value = {'domain': None}
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'hpe3par:group_replication': '<is> True',
'replication:sync_period': '900',
'volume_type': self.volume_type_tiramisu}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
return_model = self.driver.create_volume(self.volume_replicated)
expected = [
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HPE3PAR_CPG,
2048, {
'comment': mock.ANY,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual({'provider_location': self.CLIENT_ID},
return_model)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_with_tiramisu_volume_type_and_added_in_group(
self, _mock_volume_types, vol_ss_enable):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
mock_client.getCPG.return_value = {'domain': None}
mock_client.getRemoteCopyGroup.return_value = (
{'volumes': [{'name': self.VOLUME_3PAR_NAME}]})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
mock_create_client.return_value = mock_client
group = self.fake_group_object()
group.is_replicated = True
group.replication_status = fields.ReplicationStatus.ENABLED
group.volume_types = [self.volume_type_tiramisu]
volume = self.volume_tiramisu.copy()
volume['group'] = group
volume['group_id'] = group.id
return_model = self.driver.create_volume(volume)
expected = [
mock.call.createVolume(
self.VOLUME_3PAR_NAME, HPE3PAR_CPG, 2048,
{'comment': mock.ANY,
'tpvv': True,
'tdvv': False,
'snapCPG': HPE3PAR_CPG_SNAP}),
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_GROUP_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': 'target'}],
optional={'volumeAutoCreation': True}),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.startRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual({'provider_location': self.CLIENT_ID,
'replication_status':
fields.ReplicationStatus.ENABLED},
return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_revert_to_snapshot_of_volume_in_group(self, _mock_volume_types):
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_replicated}}
mock_client = self.setup_driver()
mock_client.isOnlinePhysicalCopy.return_value = False
mock_client.getStorageSystemInfo.return_value = mock.ANY
mock_client.promoteVirtualCopy.return_value = {'taskid': 1}
mock_client.getTask.return_value = {'status': 1}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = self.volume_tiramisu.copy()
group = self.fake_group_object()
group.is_replicated = True
group.replication_status = fields.ReplicationStatus.ENABLED
group.volume_types = [self.volume_type_tiramisu]
volume['group'] = group
self.driver.revert_to_snapshot(
self.ctxt,
volume,
self.snapshot)
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.isOnlinePhysicalCopy(self.VOLUME_3PAR_NAME),
mock.call.promoteVirtualCopy(
'oss-L4I73ONuTci9Fd4ceij-MQ',
optional={'allowRemoteCopyParent': True}),
mock.call.getTask(1),
mock.call.startRemoteCopy(self.RCG_3PAR_GROUP_NAME)
]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_failover_host_with_group_exist(self, _mock_volume_types):
# periodic vs. sync is not relevant when conducting a failover. We
# will just use periodic.
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
valid_backend_id = (
self.replication_targets[0]['backend_id'])
# create a group
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
volume = self.volume_tiramisu.copy()
volume['group'] = group
volume['group_id'] = group.id
group_model = {
'group_id': group.id,
'updates': {'replication_status':
fields.ReplicationStatus.FAILED_OVER}}
expected_model = (self.REPLICATION_BACKEND_ID,
[{'updates': {
'id': self.VOLUME_ID,
'replication_status':
fields.ReplicationStatus.FAILED_OVER,
'provider_location':
self.CLIENT_ID,
'replication_driver_data':
self.REPLICATION_CLIENT_ID},
'volume_id': self.VOLUME_ID}],
[group_model])
return_model = self.driver.failover_host(
context.get_admin_context(), [volume],
valid_backend_id, [group])
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_model, return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_replication_failback_with_group_exist(self, _mock_volume_types):
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
# create a group
group = self.fake_group_object()
group.is_replicated = True
group.volume_types = [self.volume_type_tiramisu]
volume = self.volume_tiramisu.copy()
volume['group'] = group
volume['group_id'] = group.id
volume['replication_status'] = 'failed-over'
volume['replication_driver_data'] = self.REPLICATION_CLIENT_ID
group_model = {
'group_id': group.id,
'updates': {'replication_status':
fields.ReplicationStatus.ENABLED}}
expected_model = (None,
[{'updates': {'id': self.VOLUME_ID,
'replication_status':
fields.ReplicationStatus.ENABLED,
'provider_location':
self.CLIENT_ID,
'replication_driver_data':
self.REPLICATION_CLIENT_ID},
'volume_id': self.VOLUME_ID}],
[group_model])
return_model = self.driver.failover_host(
context.get_admin_context(), [volume], 'default', [group])
self.assertEqual(expected_model, return_model)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'get_volume_settings_from_type')
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'create_group')
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_group_with_replication_from_src_group(
self, _mock_type, _src_group, cg_ss_enable, vol_ss_enable,
typ_info):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
mock_client.getRemoteCopyGroup.return_value = (
{'volumes': [{'name': self.VOLUME_3PAR_NAME}]})
mock_client.getCPG.return_value = {'domain': None}
task_id = 1
mock_client.copyVolume.return_value = {'taskid': task_id}
mock_client.getTask.return_value = {'status': 1}
type_info = {'cpg': 'OpenStackCPG',
'tpvv': True,
'tdvv': False,
'snap_cpg': 'OpenStackCPG',
'hpe3par_keys': {'group_replication': '<is> True'}}
_mock_type.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'hpe3par:group_replication': '<is> True',
'volume_type': self.volume_type_tiramisu}}
typ_info.return_value = type_info
source_volume = self.volume_src_cg
volume = self.volume_tiramisu
volume['source_volid'] = source_volume['id']
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume.get('id'))
mock_client.getVolume.return_value = {'copyOf': vol_name}
group_snap_optional = (
{'expirationHours': 1})
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
group = self.fake_group_object()
_src_group.return_value = group
group.is_replicated = True
source_grp = self.fake_group_object(
grp_id=self.SRC_CONSIS_GROUP_ID)
source_grp.is_replicated = True
expected = [
mock.call.createSnapshotOfVolumeSet(
mock.ANY,
self.SRC_CONSIS_GROUP_NAME,
optional=group_snap_optional),
mock.call.stopRemoteCopy(self.RCG_3PAR_GROUP_NAME),
mock.call.getVolume(mock.ANY),
mock.call.copyVolume(
mock.ANY,
self.VOLUME_NAME_3PAR,
HPE3PAR_CPG,
{'snapCPG': HPE3PAR_CPG, 'online': True,
'tpvv': mock.ANY, 'tdvv': mock.ANY}),
mock.call.getTask(task_id),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.addVolumeToRemoteCopyGroup(
self.RCG_3PAR_GROUP_NAME,
self.VOLUME_3PAR_NAME,
[{'secVolumeName': self.VOLUME_3PAR_NAME,
'targetName': 'target'}],
optional={'volumeAutoCreation': True}),
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_NAME_3PAR),
mock.call.getRemoteCopyGroup(self.RCG_3PAR_GROUP_NAME),
mock.call.startRemoteCopy(self.RCG_3PAR_GROUP_NAME)]
# Create a consistency group from a source consistency group.
self.driver.create_group_from_src(
context.get_admin_context(), group,
[volume], source_group=source_grp,
source_vols=[source_volume])
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'get_volume_settings_from_type')
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_from_src(self, cg_ss_enable, vol_ss_enable,
typ_info):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
type_info = {'cpg': 'OpenStackCPG',
'tpvv': True,
'tdvv': False,
'snap_cpg': 'OpenStackCPG',
'hpe3par_keys': {}}
typ_info.return_value = type_info
source_volume = self.volume_src_cg
volume = self.fake_volume_object(source_volid=source_volume['id'])
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume.id)
mock_client.getVolume.return_value = {'copyOf': vol_name}
group_snap_comment = Comment({
"group_id": "6044fedf-c889-4752-900f-2039d247a5df",
"description": "group_snapshot",
"group_snapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2",
})
group_snap_optional = (
{'comment': group_snap_comment,
'readOnly': False})
group_comment = Comment({
'group_id': self.GROUP_ID
})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
# create a consistency group
group = self.fake_group_object()
self.driver.create_group(context.get_admin_context(), group)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=group_comment)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# add a volume to the consistency group
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[volume], remove_volumes=[])
expected = [
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_NAME_3PAR)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# create a snapshot of the consistency group
grp_snapshot = self.fake_group_snapshot_object()
self.driver.create_group_snapshot(context.get_admin_context(),
grp_snapshot, [])
expected = [
mock.call.createSnapshotOfVolumeSet(
self.CGSNAPSHOT_BASE_NAME + "-@count@",
self.CONSIS_GROUP_NAME,
optional=group_snap_optional)]
# create a consistency group from the cgsnapshot
self.driver.create_group_from_src(
context.get_admin_context(), group,
[volume], group_snapshot=grp_snapshot,
snapshots=[self.snapshot])
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'get_volume_settings_from_type')
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_from_src_group(self, cg_ss_enable, vol_ss_enable,
typ_info):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
task_id = 1
mock_client.copyVolume.return_value = {'taskid': task_id}
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
type_info = {'cpg': 'OpenStackCPG',
'tpvv': True,
'tdvv': False,
'snap_cpg': 'OpenStackCPG',
'hpe3par_keys': {}}
typ_info.return_value = type_info
source_volume = self.volume_src_cg
volume = self.fake_volume_object(source_volid=source_volume['id'])
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume.id)
mock_client.getVolume.return_value = {'copyOf': vol_name}
group_snap_optional = (
{'expirationHours': 1})
group_comment = Comment({
'group_id': self.GROUP_ID
})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
group = self.fake_group_object()
source_grp = self.fake_group_object(
grp_id=self.SRC_CONSIS_GROUP_ID)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=group_comment),
mock.call.createSnapshotOfVolumeSet(
mock.ANY,
self.SRC_CONSIS_GROUP_NAME,
optional=group_snap_optional),
mock.call.getVolume(mock.ANY),
mock.call.copyVolume(
mock.ANY,
self.VOLUME_NAME_3PAR,
HPE3PAR_CPG,
{'snapCPG': HPE3PAR_CPG, 'online': True,
'tpvv': mock.ANY, 'tdvv': mock.ANY}),
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_NAME_3PAR)]
# Create a consistency group from a source consistency group.
self.driver.create_group_from_src(
context.get_admin_context(), group,
[volume], source_group=source_grp,
source_vols=[source_volume])
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_group(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
comment = Comment({
'group_id': self.GROUP_ID
})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
# create a consistency group
group = self.fake_group_object()
self.driver.create_group(context.get_admin_context(), group)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=comment)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# remove the consistency group
group.status = fields.GroupStatus.DELETING
self.driver.delete_group(context.get_admin_context(), group, [])
expected = [
mock.call.deleteVolumeSet(
self.CONSIS_GROUP_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_group_exceptions(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
# create a consistency group
group = self.fake_group_object()
volume = fake_volume.fake_volume_obj(context.get_admin_context())
self.driver.create_group(context.get_admin_context(), group)
# remove the consistency group
group.status = fields.GroupStatus.DELETING
# mock HTTPConflict in delete volume set
mock_client.deleteVolumeSet.side_effect = (
hpeexceptions.HTTPConflict())
# no exception should escape method
self.driver.delete_group(context.get_admin_context(), group, [])
# mock HTTPNotFound in delete volume set
mock_client.deleteVolumeSet.side_effect = (
hpeexceptions.HTTPNotFound())
# no exception should escape method
self.driver.delete_group(context.get_admin_context(), group, [])
# mock HTTPConflict in delete volume
mock_client.deleteVolume.side_effect = (
hpeexceptions.HTTPConflict())
# no exception should escape method
self.driver.delete_group(context.get_admin_context(), group,
[volume])
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_update_group_add_vol(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
volume = self.fake_volume_object()
comment = Comment({
'group_id': self.GROUP_ID
})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
# create a consistency group
group = self.fake_group_object()
self.driver.create_group(context.get_admin_context(), group)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=comment)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# add a volume to the consistency group
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[volume], remove_volumes=[])
expected = [
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_NAME_3PAR)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_update_group_remove_vol(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
volume = self.fake_volume_object()
comment = Comment({
'group_id': self.GROUP_ID
})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
# create a consistency group
group = self.fake_group_object()
self.driver.create_group(context.get_admin_context(), group)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=comment)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# add a volume to the consistency group
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[volume], remove_volumes=[])
expected = [
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_NAME_3PAR)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# remove the volume from the consistency group
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[], remove_volumes=[volume])
expected = [
mock.call.removeVolumeFromVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_NAME_3PAR)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_create_group_snapshot(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
volume = self.fake_volume_object()
cg_comment = Comment({
'group_id': self.GROUP_ID
})
group_snap_comment = Comment({
"group_id": "6044fedf-c889-4752-900f-2039d247a5df",
"description": "group_snapshot",
"group_snapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"})
cgsnap_optional = (
{'comment': group_snap_comment,
'readOnly': False})
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
# create a consistency group
group = self.fake_group_object()
self.driver.create_group(context.get_admin_context(), group)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=cg_comment)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# add a volume to the consistency group
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[volume], remove_volumes=[])
expected = [
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_NAME_3PAR)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# create a snapshot of the consistency group
group_snapshot = self.fake_group_snapshot_object()
self.driver.create_group_snapshot(context.get_admin_context(),
group_snapshot, [])
expected = [
mock.call.createSnapshotOfVolumeSet(
self.CGSNAPSHOT_BASE_NAME + "-@count@",
self.CONSIS_GROUP_NAME,
optional=cgsnap_optional)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.'
'is_volume_group_snap_type')
@mock.patch('cinder.volume.volume_utils.is_group_a_cg_snapshot_type')
def test_delete_group_snapshot(self, cg_ss_enable, vol_ss_enable):
cg_ss_enable.return_value = True
vol_ss_enable.return_value = True
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
volume = self.fake_volume_object()
group_snapshot = self.fake_group_snapshot_object()
cg_comment = Comment({
'group_id': self.GROUP_ID
})
group_snap_comment = Comment({
"group_id": "6044fedf-c889-4752-900f-2039d247a5df",
"description": "group_snapshot",
"group_snapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"})
group_snap_optional = {'comment': group_snap_comment,
'readOnly': False}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.getCPG.return_value = {'domain': None}
# create a consistency group
group = self.fake_group_object()
self.driver.create_group(context.get_admin_context(), group)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.createVolumeSet(
self.CONSIS_GROUP_NAME,
domain=None,
comment=cg_comment)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# add a volume to the consistency group
self.driver.update_group(context.get_admin_context(), group,
add_volumes=[volume], remove_volumes=[])
expected = [
mock.call.addVolumeToVolumeSet(
self.CONSIS_GROUP_NAME,
self.VOLUME_NAME_3PAR)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
# create a snapshot of the consistency group
self.driver.create_group_snapshot(context.get_admin_context(),
group_snapshot, [])
expected = [
mock.call.createSnapshotOfVolumeSet(
self.CGSNAPSHOT_BASE_NAME + "-@count@",
self.CONSIS_GROUP_NAME,
optional=group_snap_optional)]
# delete the snapshot of the consistency group
group_snapshot.status = fields.GroupSnapshotStatus.DELETING
self.driver.delete_group_snapshot(context.get_admin_context(),
group_snapshot, [])
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_failover_host(self, _mock_volume_types):
# periodic vs. sync is not relevant when conducting a failover. We
# will just use periodic.
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
valid_backend_id = (
self.replication_targets[0]['backend_id'])
invalid_backend_id = 'INVALID'
volumes = [self.volume_replicated]
# Test invalid secondary target.
self.assertRaises(
exception.InvalidReplicationTarget,
self.driver.failover_host,
context.get_admin_context(),
volumes,
invalid_backend_id)
# Test no secondary target.
self.assertRaises(
exception.InvalidReplicationTarget,
self.driver.failover_host,
context.get_admin_context(),
volumes,
None)
# Test a successful failover.
expected_model = (self.REPLICATION_BACKEND_ID,
[{'updates': {'replication_status':
'failed-over',
'replication_driver_data':
self.REPLICATION_CLIENT_ID},
'volume_id': self.VOLUME_ID}],
[])
return_model = self.driver.failover_host(
context.get_admin_context(),
volumes,
valid_backend_id)
expected = [
mock.call.stopRemoteCopy(self.RCG_3PAR_NAME)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_model, return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_replication_failback_ready(self, _mock_volume_types):
# Managed vs. unmanaged and periodic vs. sync are not relevant when
# failing back a volume.
# We will use managed and periodic as the default.
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
# Test a successful fail-back.
volume = self.volume_replicated.copy()
volume['replication_status'] = 'failed-over'
return_model = self.driver.failover_host(
context.get_admin_context(),
[volume],
'default')
expected_model = (None,
[{'updates': {'replication_status':
'available',
'replication_driver_data':
self.CLIENT_ID},
'volume_id': self.VOLUME_ID}],
[])
self.assertEqual(expected_model, return_model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_replication_failback_not_ready(self, _mock_volume_types):
# Managed vs. unmanaged and periodic vs. sync are not relevant when
# failing back a volume.
# We will use managed and periodic as the default.
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'periodic',
'replication:sync_period': '900',
'volume_type': self.volume_type_replicated}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_client.getRemoteCopyGroup.side_effect = (
exception.VolumeBackendAPIException(
"Error: Remote Copy Group not Ready."))
mock_replication_client.return_value = mock_replicated_client
# Test an unsuccessful fail-back.
volume = self.volume_replicated.copy()
volume['replication_status'] = 'failed-over'
self.assertRaises(
exception.InvalidReplicationTarget,
self.driver.failover_host,
context.get_admin_context(),
[volume],
'default')
def test_get_pool_with_existing_volume(self):
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
actual_cpg = self.driver.get_pool(self.volume)
expected_cpg = HPE3PAR_CPG
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME)
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_cpg, actual_cpg)
def test_get_pool_with_non_existing_volume(self):
mock_client = self.setup_driver()
mock_client.getVolume.side_effect = hpeexceptions.HTTPNotFound
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME)
]
try:
self.assertRaises(
hpeexceptions.HTTPNotFound,
self.driver.get_pool,
self.volume)
except exception.InvalidVolume:
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_driver_login_with_wrong_credential_and_replication_disabled(self):
mock_client = self.setup_driver()
mock_client.login.side_effect = hpeexceptions.HTTPUnauthorized
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
expected = [
mock.call.login(HPE3PAR_USER_NAME, HPE3PAR_USER_PASS)
]
self.assertRaises(
exception.InvalidInput,
self.driver._login)
mock_client.assert_has_calls(expected)
def test_driver_login_with_wrong_credential_and_replication_enabled(self):
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'periodic'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_replicated_client = self.setup_driver(config=conf)
mock_client.login.side_effect = hpeexceptions.HTTPUnauthorized
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
expected = [
mock.call.login(HPE3PAR_USER_NAME, HPE3PAR_USER_PASS)
]
common = self.driver._login()
mock_client.assert_has_calls(
expected)
self.assertTrue(common._replication_enabled)
def test_init_vendor_properties(self):
conf = self.setup_configuration()
mock_client = self.setup_driver(config=conf)
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
with mock.patch.object(self.driver,
'get_volume_stats') as stats:
stats.return_value = {}
# calling vendor properties from driver.
self.driver.init_capabilities()
# calling vendor properties from specific 3par driver.
properties, vendor_name = self.driver._init_vendor_properties()
for key in self.driver.capabilities['properties']:
new_key = key.replace('_', ':')
if 'HP:3PAR' in new_key:
self.assertIn(new_key, properties)
@ddt.ddt
class TestHPE3PARFCDriver(HPE3PARBaseDriver):
properties = {
'driver_volume_type': 'fibre_channel',
'data': {
'encrypted': False,
'target_lun': 90,
'target_wwn': ['0987654321234', '123456789000987'],
'target_discovered': True,
'initiator_target_map': {'123456789012345':
['0987654321234', '123456789000987'],
'123456789054321':
['0987654321234', '123456789000987'],
}}}
def setup_driver(self, config=None, mock_conf=None, wsapi_version=None):
self.ctxt = context.get_admin_context()
mock_client = self.setup_mock_client(
conf=config,
m_conf=mock_conf,
driver=hpefcdriver.HPE3PARFCDriver)
if wsapi_version:
mock_client.getWsApiVersion.return_value = (
wsapi_version)
else:
mock_client.getWsApiVersion.return_value = (
self.wsapi_version_latest)
expected = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
return mock_client
@ddt.data('volume', 'volume_name_id')
def test_initialize_connection(self, volume_attr):
volume = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 7,
'slot': 1},
'vendor': None,
'wwn': self.wwn[0]},
{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 6,
'slot': 1},
'vendor': None,
'wwn': self.wwn[1]}]}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': vol_name,
'remoteName': self.wwn[1],
'lun': 90, 'type': 0}],
[{'active': True,
'volumeName': vol_name,
'remoteName': self.wwn[0],
'lun': 90, 'type': 0}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': vol_name,
'lun_id': 90,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
expected_properties = {
'driver_volume_type': 'fibre_channel',
'data': {
'encrypted': False,
'target_lun': 90,
'target_wwn': ['0987654321234', '123456789000987'],
'target_discovered': True,
'initiator_target_map':
{'123456789012345': ['0987654321234', '123456789000987'],
'123456789054321': ['0987654321234', '123456789000987']}}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
result = self.driver.initialize_connection(
volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume(vol_name),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN(
vol_name,
auto=True,
hostname=self.FAKE_HOST,
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(expected_properties, result)
def test_initialize_connection_single_path(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'vendor': None,
'wwn': self.wwn[0]}]}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'portPos': {'node': 7, 'slot': 1, 'cardPort': 1},
'remoteName': self.wwn[0],
'lun': 90, 'type': 0}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': 90,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
expected_properties = {
'driver_volume_type': 'fibre_channel',
'data': {
'encrypted': False,
'target_lun': 90,
'target_wwn': ['0987654321234'],
'target_discovered': True,
'initiator_target_map':
{'123456789012345': ['0987654321234']}}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
result = self.driver.initialize_connection(
self.volume,
self.connector.copy())
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST,
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(expected_properties, result)
@mock.patch('cinder.zonemanager.utils.create_lookup_service')
def test_initialize_connection_with_lookup_single_nsp(self, mock_lookup):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
class fake_lookup_object(object):
def get_device_mapping_from_network(self, connector, target_wwns):
fake_map = {
'FAB_1': {
'target_port_wwn_list': ['0987654321234'],
'initiator_port_wwn_list': ['123456789012345']
}
}
return fake_map
mock_lookup.return_value = fake_lookup_object()
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 1,
'slot': 2},
'vendor': None,
'wwn': self.wwn[0]}]}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': 90, 'type': 0,
'portPos': {'cardPort': 1, 'node': 7, 'slot': 1}}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': 90,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
connector = {'ip': '10.0.0.2',
'initiator': 'iqn.1993-08.org.debian:01:222',
'wwpns': [self.wwn[0]],
'wwnns': ["223456789012345"],
'host': self.FAKE_HOST}
expected_properties = {
'driver_volume_type': 'fibre_channel',
'data': {
'encrypted': False,
'target_lun': 90,
'target_wwn': ['0987654321234'],
'target_discovered': True,
'initiator_target_map': {'123456789012345':
['0987654321234']
}}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
result = self.driver.initialize_connection(self.volume, connector)
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.ANY,
mock.call.getHost(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST,
portPos={'node': 7, 'slot': 1, 'cardPort': 1},
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(expected_properties, result)
def test_initialize_connection_single_path_target_nsp(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.configuration.hpe3par_target_nsp = '2:1:2'
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'vendor': None,
'wwn': self.wwn[0]}]}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'portPos': {'node': 7, 'slot': 1, 'cardPort': 1},
'remoteName': self.wwn[0],
'lun': 90, 'type': 0}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': 90,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
user_target_wwn = '0987654321234'
expected_properties = {
'driver_volume_type': 'fibre_channel',
'data': {
'encrypted': False,
'target_lun': 90,
'target_wwn': [user_target_wwn],
'target_discovered': True,
'initiator_target_map':
{'123456789012345': [user_target_wwn]}}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
result = self.driver.initialize_connection(
self.volume,
self.connector.copy())
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.getPorts(),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST,
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(expected_properties, result)
def test_initialize_connection_encrypted(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'vendor': None,
'wwn': self.wwn[0]},
{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'vendor': None,
'wwn': self.wwn[1]}]}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': 90, 'type': 0,
'remoteName': self.wwn[1]}],
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'remoteName': self.wwn[0],
'lun': 90, 'type': 0}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': 90,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
expected_properties = {
'driver_volume_type': 'fibre_channel',
'data': {
'encrypted': True,
'target_lun': 90,
'target_wwn': ['0987654321234', '123456789000987'],
'target_discovered': True,
'initiator_target_map':
{'123456789012345': ['0987654321234', '123456789000987'],
'123456789054321': ['0987654321234', '123456789000987']}}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
result = self.driver.initialize_connection(
self.volume_encrypted,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST,
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(expected_properties, result)
@mock.patch.object(volume_types, 'get_volume_type')
def test_initialize_connection_peer_persistence(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
self.replication_targets[0]['quorum_witness_ip'] = '10.50.3.192'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'volume_type': self.volume_type_replicated}}
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 7,
'slot': 1},
'vendor': None,
'wwn': self.wwn[0]},
{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 6,
'slot': 1},
'vendor': None,
'wwn': self.wwn[1]}]}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'remoteName': self.wwn[1],
'lun': 90, 'type': 0}],
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'remoteName': self.wwn[0],
'lun': 90, 'type': 0}]]
mock_replicated_client.getHostVLUNs.side_effect = (
mock_client.getHostVLUNs.side_effect)
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': 90,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
mock_replicated_client.createVLUN.return_value = location
expected_properties = {
'driver_volume_type': 'fibre_channel',
'data': {
'encrypted': False,
'target_lun': 90,
'target_wwn': ['0987654321234', '123456789000987',
'0987654321234', '123456789000987'],
'target_discovered': True,
'initiator_target_map':
{'123456789012345': ['0987654321234', '123456789000987',
'0987654321234', '123456789000987'],
'123456789054321': ['0987654321234', '123456789000987',
'0987654321234', '123456789000987']}}}
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
volume = self.volume
volume['replication_status'] = 'enabled'
result = self.driver.initialize_connection(
volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST,
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(expected_properties, result)
@ddt.data('volume', 'volume_name_id')
def test_terminate_connection(self, volume_attr):
volume = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
effects = [
[{'active': False, 'volumeName': vol_name,
'lun': None, 'type': 0}],
hpeexceptions.HTTPNotFound,
hpeexceptions.HTTPNotFound]
mock_client.getHostVLUNs.side_effect = effects
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
expected = [
mock.call.queryHost(wwns=['123456789012345', '123456789054321']),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
vol_name,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteHost(self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.getPorts()]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
conn_info = self.driver.terminate_connection(volume,
self.connector)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertIn('data', conn_info)
self.assertIn('initiator_target_map', conn_info['data'])
mock_client.reset_mock()
mock_client.getHostVLUNs.side_effect = effects
# mock some deleteHost exceptions that are handled
delete_with_vlun = hpeexceptions.HTTPConflict(
error={'message': "has exported VLUN"})
delete_with_hostset = hpeexceptions.HTTPConflict(
error={'message': "host is a member of a set"})
mock_client.deleteHost = mock.Mock(
side_effect=[delete_with_vlun, delete_with_hostset])
conn_info = self.driver.terminate_connection(volume,
self.connector)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
mock_client.getHostVLUNs.side_effect = effects
conn_info = self.driver.terminate_connection(volume,
self.connector)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_force_detach_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVLUNs.return_value = {
'members': [{
'active': False,
'volumeName': self.VOLUME_3PAR_NAME,
'hostname': self.FAKE_HOST,
'lun': None, 'type': 0}]}
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = hpeexceptions.HTTPNotFound
expected = [
mock.call.getVLUNs(),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteHost(self.FAKE_HOST)]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.terminate_connection(self.volume, None)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch('cinder.zonemanager.utils.create_lookup_service')
def test_terminate_connection_with_lookup(self, mock_lookup):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
class fake_lookup_object(object):
def get_device_mapping_from_network(self, connector, target_wwns):
fake_map = {
'FAB_1': {
'target_port_wwn_list': ['0987654321234'],
'initiator_port_wwn_list': ['123456789012345']
}
}
return fake_map
mock_lookup.return_value = fake_lookup_object()
mock_client = self.setup_driver()
effects = [
[{'active': False, 'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0}],
hpeexceptions.HTTPNotFound,
hpeexceptions.HTTPNotFound]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = effects
expected = [
mock.call.queryHost(wwns=['123456789012345', '123456789054321']),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteHost(self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.getPorts()]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
conn_info = self.driver.terminate_connection(self.volume,
self.connector)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertIn('data', conn_info)
self.assertIn('initiator_target_map', conn_info['data'])
mock_client.reset_mock()
mock_client.getHostVLUNs.side_effect = effects
# mock some deleteHost exceptions that are handled
delete_with_vlun = hpeexceptions.HTTPConflict(
error={'message': "has exported VLUN"})
delete_with_hostset = hpeexceptions.HTTPConflict(
error={'message': "host is a member of a set"})
mock_client.deleteHost = mock.Mock(
side_effect=[delete_with_vlun, delete_with_hostset])
conn_info = self.driver.terminate_connection(self.volume,
self.connector)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
mock_client.getHostVLUNs.side_effect = effects
conn_info = self.driver.terminate_connection(self.volume,
self.connector)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_terminate_connection_more_vols(self):
mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
# mock more than one vlun on the host (don't even try to remove host)
mock_client.getHostVLUNs.return_value = \
[
{'active': False,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0},
{'active': True,
'volumeName': 'there-is-another-volume',
'remoteName': '123456789012ABC',
'lun': None, 'type': 0},
{'active': True,
'volumeName': 'there-is-another-volume',
'remoteName': '123456789012ABC',
'lun': None, 'type': 0},
]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
expect_less = [
mock.call.queryHost(wwns=['123456789012345', '123456789054321']),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.modifyHost(
'fakehost',
{'FCWWNs': ['123456789012345', '123456789054321'],
'pathOperation': self.mock_client_conf['HOST_EDIT_REMOVE']}),
mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.getPorts()]
expect_conn = {
'driver_volume_type': 'fibre_channel',
'data': {'initiator_target_map':
{'123456789012345': ['0987654321234', '123456789000987'],
'123456789054321': ['0987654321234', '123456789000987']},
'target_wwn': ['0987654321234', '123456789000987']}}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
conn_info = self.driver.terminate_connection(self.volume,
self.connector)
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expect_less +
self.standard_logout)
self.assertEqual(expect_conn, conn_info)
@mock.patch.object(volume_types, 'get_volume_type')
def test_terminate_connection_peer_persistence(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
self.replication_targets[0]['quorum_witness_ip'] = '10.50.3.192'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'volume_type': self.volume_type_replicated}}
effects = [
[{'active': False, 'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0}],
hpeexceptions.HTTPNotFound,
hpeexceptions.HTTPNotFound]
mock_client.getHostVLUNs.side_effect = effects
mock_replicated_client.getHostVLUNs.side_effect = effects
getHost_side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 7,
'slot': 1},
'vendor': None,
'wwn': self.wwn[0]},
{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 6,
'slot': 1},
'vendor': None,
'wwn': self.wwn[1]}]}]
queryHost_return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHost.side_effect = getHost_side_effect
mock_client.queryHost.return_value = queryHost_return_value
mock_replicated_client.getHost.side_effect = getHost_side_effect
mock_replicated_client.queryHost.return_value = queryHost_return_value
expected = [
mock.call.queryHost(wwns=['123456789012345', '123456789054321']),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteHost(self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.getPorts()]
volume = self.volume
volume['replication_status'] = 'enabled'
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
conn_info = self.driver.terminate_connection(
volume, self.connector_multipath_enabled)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertIn('data', conn_info)
self.assertIn('initiator_target_map', conn_info['data'])
mock_client.reset_mock()
mock_replicated_client.reset_mock()
mock_client.getHostVLUNs.side_effect = effects
mock_replicated_client.getHostVLUNs.side_effect = effects
# mock some deleteHost exceptions that are handled
delete_with_vlun = hpeexceptions.HTTPConflict(
error={'message': "has exported VLUN"})
delete_with_hostset = hpeexceptions.HTTPConflict(
error={'message': "host is a member of a set"})
mock_client.deleteHost = mock.Mock(
side_effect=[delete_with_vlun, delete_with_hostset])
conn_info = self.driver.terminate_connection(
volume, self.connector_multipath_enabled)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
mock_client.reset_mock()
mock_replicated_client.reset_mock()
mock_client.getHostVLUNs.side_effect = effects
mock_replicated_client.getHostVLUNs.side_effect = effects
conn_info = self.driver.terminate_connection(
volume, self.connector_multipath_enabled)
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
def test_get_3par_host_from_wwn_iqn(self):
mock_client = self.setup_driver()
mock_client.getHosts.return_value = {
'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 1,
'slot': 2},
'vendor': None,
'wwn': '123ab6789012345'}]}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
hostname = mock_client._get_3par_hostname_from_wwn_iqn(
wwns=['123AB6789012345', '123CD6789054321'],
iqns=None)
self.assertIsNotNone(hostname)
def test_get_volume_stats1(self):
# setup_mock_client drive with the configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.filter_function = FILTER_FUNCTION
config.goodness_function = GOODNESS_FUNCTION
mock_client = self.setup_driver(config=config)
mock_client.getCPG.return_value = self.cpgs[0]
# Purposely left out the Priority Optimization license in
# getStorageSystemInfo to test that QoS_support returns False.
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234',
'licenseInfo': {
'licenses': [{'name': 'Remote Copy'},
{'name': 'Thin Provisioning (102400G)'},
{'name': 'System Reporter'}]
}
}
# cpg has no limit
mock_client.getCPGAvailableSpace.return_value = {
"capacityEfficiency": {u'compaction': 594.4},
"rawFreeMiB": 1024.0 * 6,
"usableFreeMiB": 1024.0 * 3
}
stat_capabilities = {
THROUGHPUT: 0,
BANDWIDTH: 0,
LATENCY: 0,
IO_SIZE: 0,
QUEUE_LENGTH: 0,
AVG_BUSY_PERC: 0
}
mock_client.getCPGStatData.return_value = stat_capabilities
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
stats = self.driver.get_volume_stats(True)
const = 0.0009765625
self.assertEqual('FC', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertTrue(stats['pools'][0]['thin_provisioning_support'])
self.assertTrue(stats['pools'][0]['thick_provisioning_support'])
self.assertFalse(stats['pools'][0]['QoS_support'])
self.assertEqual(86.0,
stats['pools'][0]['provisioned_capacity_gb'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual('up', stats['pools'][0]['backend_state'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertEqual(stat_capabilities[THROUGHPUT],
stats['pools'][0][THROUGHPUT])
self.assertEqual(stat_capabilities[BANDWIDTH],
stats['pools'][0][BANDWIDTH])
self.assertEqual(stat_capabilities[LATENCY],
stats['pools'][0][LATENCY])
self.assertEqual(stat_capabilities[IO_SIZE],
stats['pools'][0][IO_SIZE])
self.assertEqual(stat_capabilities[QUEUE_LENGTH],
stats['pools'][0][QUEUE_LENGTH])
self.assertEqual(stat_capabilities[AVG_BUSY_PERC],
stats['pools'][0][AVG_BUSY_PERC])
expected = [
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPGStatData(HPE3PAR_CPG, 'daily', '7d'),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2),
mock.call.getCPGStatData(HPE3PAR_CPG2, 'daily', '7d'),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
stats = self.driver.get_volume_stats(True)
self.assertEqual('FC', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertTrue(stats['pools'][0]['thin_provisioning_support'])
self.assertTrue(stats['pools'][0]['thick_provisioning_support'])
self.assertFalse(stats['pools'][0]['QoS_support'])
self.assertEqual(86.0,
stats['pools'][0]['provisioned_capacity_gb'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertEqual(stat_capabilities[THROUGHPUT],
stats['pools'][0][THROUGHPUT])
self.assertEqual(stat_capabilities[BANDWIDTH],
stats['pools'][0][BANDWIDTH])
self.assertEqual(stat_capabilities[LATENCY],
stats['pools'][0][LATENCY])
self.assertEqual(stat_capabilities[IO_SIZE],
stats['pools'][0][IO_SIZE])
self.assertEqual(stat_capabilities[QUEUE_LENGTH],
stats['pools'][0][QUEUE_LENGTH])
self.assertEqual(stat_capabilities[AVG_BUSY_PERC],
stats['pools'][0][AVG_BUSY_PERC])
cpg2 = self.cpgs[0].copy()
cpg2.update({'SDGrowth': {'limitMiB': 8192}})
mock_client.getCPG.return_value = cpg2
stats = self.driver.get_volume_stats(True)
self.assertEqual('FC', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertTrue(stats['pools'][0]['thin_provisioning_support'])
self.assertTrue(stats['pools'][0]['thick_provisioning_support'])
self.assertFalse(stats['pools'][0]['QoS_support'])
total_capacity_gb = 8192 * const
self.assertEqual(total_capacity_gb,
stats['pools'][0]['total_capacity_gb'])
free_capacity_gb = int(
(8192 - (self.cpgs[0]['UsrUsage']['usedMiB'] +
self.cpgs[0]['SDUsage']['usedMiB'])) * const)
self.assertEqual(free_capacity_gb,
stats['pools'][0]['free_capacity_gb'])
provisioned_capacity_gb = int(
(self.cpgs[0]['UsrUsage']['totalMiB'] +
self.cpgs[0]['SAUsage']['totalMiB'] +
self.cpgs[0]['SDUsage']['totalMiB']) * const)
self.assertEqual(provisioned_capacity_gb,
stats['pools'][0]['provisioned_capacity_gb'])
cap_util = (float(total_capacity_gb - free_capacity_gb) /
float(total_capacity_gb)) * 100
self.assertEqual(cap_util,
stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertEqual(stat_capabilities[THROUGHPUT],
stats['pools'][0][THROUGHPUT])
self.assertEqual(stat_capabilities[BANDWIDTH],
stats['pools'][0][BANDWIDTH])
self.assertEqual(stat_capabilities[LATENCY],
stats['pools'][0][LATENCY])
self.assertEqual(stat_capabilities[IO_SIZE],
stats['pools'][0][IO_SIZE])
self.assertEqual(stat_capabilities[QUEUE_LENGTH],
stats['pools'][0][QUEUE_LENGTH])
self.assertEqual(stat_capabilities[AVG_BUSY_PERC],
stats['pools'][0][AVG_BUSY_PERC])
common.client.deleteCPG(HPE3PAR_CPG)
common.client.createCPG(HPE3PAR_CPG, {})
def test_get_volume_stats2(self):
# Testing when the API_VERSION is incompatible with getCPGStatData
srstatld_api_version = 30201200
pre_srstatld_api_version = srstatld_api_version - 1
wsapi = {'build': pre_srstatld_api_version}
config = self.setup_configuration()
config.filter_function = FILTER_FUNCTION
config.goodness_function = GOODNESS_FUNCTION
mock_client = self.setup_driver(config=config, wsapi_version=wsapi)
mock_client.getCPG.return_value = self.cpgs[0]
# Purposely left out the Thin Provisioning license in
# getStorageSystemInfo to test that thin_provisioning_support returns
# False.
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234',
'licenseInfo': {
'licenses': [{'name': 'Remote Copy'},
{'name': 'Priority Optimization'}]
}
}
# cpg has no limit
mock_client.getCPGAvailableSpace.return_value = {
"capacityEfficiency": {u'compaction': 594.4},
"rawFreeMiB": 1024.0 * 6,
"usableFreeMiB": 1024.0 * 3
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
stats = self.driver.get_volume_stats(True)
self.assertEqual('FC', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertFalse(stats['pools'][0]['thin_provisioning_support'])
self.assertTrue(stats['pools'][0]['QoS_support'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertIsNone(stats['pools'][0][THROUGHPUT])
self.assertIsNone(stats['pools'][0][BANDWIDTH])
self.assertIsNone(stats['pools'][0][LATENCY])
self.assertIsNone(stats['pools'][0][IO_SIZE])
self.assertIsNone(stats['pools'][0][QUEUE_LENGTH])
self.assertIsNone(stats['pools'][0][AVG_BUSY_PERC])
expected = [
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_get_volume_stats3(self):
# Testing when the client version is incompatible with getCPGStatData
# setup_mock_client drive with the configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.filter_function = FILTER_FUNCTION
config.goodness_function = GOODNESS_FUNCTION
mock_client = self.setup_driver(config=config,
wsapi_version=self.wsapi_version_312)
mock_client.getCPG.return_value = self.cpgs[0]
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234'
}
# cpg has no limit
mock_client.getCPGAvailableSpace.return_value = {
"capacityEfficiency": {u'compaction': 594.4},
"rawFreeMiB": 1024.0 * 6,
"usableFreeMiB": 1024.0 * 3
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
stats = self.driver.get_volume_stats(True)
self.assertEqual('FC', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertIsNone(stats['pools'][0][THROUGHPUT])
self.assertIsNone(stats['pools'][0][BANDWIDTH])
self.assertIsNone(stats['pools'][0][LATENCY])
self.assertIsNone(stats['pools'][0][IO_SIZE])
self.assertIsNone(stats['pools'][0][QUEUE_LENGTH])
self.assertIsNone(stats['pools'][0][AVG_BUSY_PERC])
expected = [
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_get_volume_stats4(self):
# Testing get_volume_stats() when System Reporter license is not active
# setup_mock_client drive with the configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.filter_function = FILTER_FUNCTION
config.goodness_function = GOODNESS_FUNCTION
mock_client = self.setup_driver(config=config)
mock_client.getCPG.return_value = self.cpgs[0]
# Purposely left out the System Reporter license in
# getStorageSystemInfo to test sr_support
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234',
'licenseInfo': {
'licenses': [{'name': 'Remote Copy'},
{'name': 'Priority Optimization'},
{'name': 'Thin Provisioning'}]
}
}
# cpg has no limit
mock_client.getCPGAvailableSpace.return_value = {
"capacityEfficiency": {u'compaction': 594.4},
"rawFreeMiB": 1024.0 * 6,
"usableFreeMiB": 1024.0 * 3
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
stats = self.driver.get_volume_stats(True)
self.assertEqual('FC', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertTrue(stats['pools'][0]['thin_provisioning_support'])
self.assertTrue(stats['pools'][0]['QoS_support'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertIsNone(stats['pools'][0][THROUGHPUT])
self.assertIsNone(stats['pools'][0][BANDWIDTH])
self.assertIsNone(stats['pools'][0][LATENCY])
self.assertIsNone(stats['pools'][0][IO_SIZE])
self.assertIsNone(stats['pools'][0][QUEUE_LENGTH])
self.assertIsNone(stats['pools'][0][AVG_BUSY_PERC])
expected = [
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_get_volume_stats5(self):
# Testing get_volume_stats(refresh=False) for cached values
config = self.setup_configuration()
self.setup_driver(config=config)
with mock.patch.object(self.driver, '_login') as login_mock, \
mock.patch.object(self.driver, '_logout') as logout_mock:
stats_mock = login_mock.return_value.get_volume_stats
stats = self.driver.get_volume_stats(True)
self.assertEqual(stats_mock.return_value, stats)
login_mock.assert_called_once_with()
stats_mock.assert_called_once_with(True, FILTER_FUNCTION,
GOODNESS_FUNCTION)
logout_mock.assert_called_once_with(login_mock.return_value)
login_mock.reset_mock()
stats_mock.reset_mock()
logout_mock.reset_mock()
cached_stats = self.driver.get_volume_stats(False)
self.assertEqual(stats, cached_stats)
login_mock.assert_not_called()
stats_mock.assert_not_called()
logout_mock.assert_not_called()
def test_create_host_with_unmanage_fc_and_manage_iscsi_hosts(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
def get_side_effect(*args):
host = {'name': None}
if args[0] == 'fake':
host['name'] = 'fake'
elif args[0] == self.FAKE_HOST:
host['name'] = self.FAKE_HOST
return host
mock_client.getHost.side_effect = get_side_effect
mock_client.queryHost.return_value = {
'members': [{
'name': 'fake'
}]
}
mock_client.getVLUN.return_value = {'lun': 186}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, cpg = self.driver._create_host(
common,
self.volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.getHost('fake')]
mock_client.assert_has_calls(expected)
self.assertEqual('fake', host['name'])
self.assertEqual(HPE3PAR_CPG, cpg)
def test_create_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 1,
'slot': 2},
'vendor': None,
'wwn': self.wwn[0]},
{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 0,
'slot': 2},
'vendor': None,
'wwn': self.wwn[1]}]}]
mock_client.queryHost.return_value = None
mock_client.getVLUN.return_value = {'lun': 186}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, cpg = self.driver._create_host(
common,
self.volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.createHost(
self.FAKE_HOST,
FCWwns=['123456789012345', '123456789054321'],
optional={'domain': None, 'persona': 2}),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
def test_create_invalid_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('Host not found.'), {
'name': 'fakehost.foo',
'FCPaths': [{'wwn': '123456789012345'}, {
'wwn': '123456789054321'}]}]
mock_client.queryHost.return_value = {
'members': [{
'name': 'fakehost.foo'
}]
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, cpg = self.driver._create_host(
common,
self.volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost('fakehost'),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.getHost('fakehost.foo')]
mock_client.assert_has_calls(expected)
self.assertEqual('fakehost.foo', host['name'])
def test_create_host_concurrent(self):
# tests concurrent requests of create host
# setup_mock_client driver with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.queryHost.side_effect = [
None,
{'members': [{'name': self.FAKE_HOST}]
}]
mock_client.createHost.side_effect = [
hpeexceptions.HTTPConflict(
{'code': EXISTENT_PATH,
'desc': 'host WWN/iSCSI name already used by another host'})]
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, cpg = self.driver._create_host(
common,
self.volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.createHost(
self.FAKE_HOST,
FCWwns=['123456789012345', '123456789054321'],
optional={'domain': None, 'persona': 2}),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
def test_create_modify_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [{
'name': self.FAKE_HOST, 'FCPaths': []},
{'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'}, {
'wwn': '123456789054321'}]}]
mock_client.queryHost.return_value = None
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, cpg = self.driver._create_host(
common,
self.volume,
self.connector_multipath_enabled)
fcwwns = ['123456789054321', '123456789012345']
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost('fakehost'),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.modifyHost('fakehost',
{'FCWWNs': mock.ANY,
'pathOperation': 1}),
mock.call.getHost('fakehost')]
# We don't know what order fcwwns is supplied in. Since
# there are only two members, test it both ways.
call1 = mock.call('fakehost', {'FCWWNs': fcwwns,
'pathOperation': 1})
fcwwns_rev = list(fcwwns)
fcwwns_rev.reverse()
call2 = mock.call('fakehost', {'FCWWNs': fcwwns_rev,
'pathOperation': 1})
self.assertTrue(call1 in mock_client.modifyHost.call_args_list or
call2 in mock_client.modifyHost.call_args_list)
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
self.assertEqual(2, len(host['FCPaths']))
def test_modify_host_with_new_wwn(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
getHost_ret1 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789054321'}]}
getHost_ret2 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}
mock_client.getHost.side_effect = [getHost_ret1, getHost_ret2]
mock_client.queryHost.return_value = None
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, cpg = self.driver._create_host(
common,
self.volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost('fakehost'),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.modifyHost(
'fakehost', {
'FCWWNs': ['123456789012345'], 'pathOperation': 1}),
mock.call.getHost('fakehost')]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
self.assertEqual(2, len(host['FCPaths']))
def test_modify_host_with_unknown_wwn_and_new_wwn(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
getHost_ret1 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789054321'},
{'wwn': 'xxxxxxxxxxxxxxx'}]}
getHost_ret2 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'},
{'wwn': 'xxxxxxxxxxxxxxx'}]}
mock_client.getHost.side_effect = [getHost_ret1, getHost_ret2]
mock_client.queryHost.return_value = None
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, cpg = self.driver._create_host(
common,
self.volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost('fakehost'),
mock.call.queryHost(wwns=['123456789012345',
'123456789054321']),
mock.call.modifyHost(
'fakehost', {
'FCWWNs': ['123456789012345'], 'pathOperation': 1}),
mock.call.getHost('fakehost')]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
self.assertEqual(3, len(host['FCPaths']))
@mock.patch.object(volume_types, 'get_volume_type')
def test_migrate_fc_volume_attached_to_iscsi_protocol(self,
_mock_volume_types):
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1
mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
protocol = "iSCSI"
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
'volume_type_id': None,
'id': HPE3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'status': 'in-use',
'host': HPE3PARBaseDriver.FAKE_HOST,
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
loc_info = 'HPE3PARDriver:1234567:CPG-FC1'
host = {'host': 'stack@3parfc1',
'capabilities': {'location_info': loc_info,
'storage_protocol': protocol}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((False, None), result)
expected = []
mock_client.assert_has_calls(expected)
def test_migrate_volume_attached(self):
self.migrate_volume_attached()
@ddt.ddt
class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
TARGET_IQN = 'iqn.2000-05.com.3pardata:21810002ac00383d'
TARGET_LUN = 186
properties = {
'driver_volume_type': 'iscsi',
'data':
{'encrypted': False,
'target_discovered': True,
'target_iqn': TARGET_IQN,
'target_lun': TARGET_LUN,
'target_portal': '1.1.1.2:1234'}}
multipath_properties = {
'driver_volume_type': 'iscsi',
'data':
{'encrypted': False,
'target_discovered': True,
'target_iqns': [TARGET_IQN],
'target_luns': [TARGET_LUN],
'target_portals': ['1.1.1.2:1234']}}
def setup_driver(self, config=None, mock_conf=None, wsapi_version=None):
self.ctxt = context.get_admin_context()
mock_client = self.setup_mock_client(
conf=config,
m_conf=mock_conf,
driver=hpedriver.HPE3PARISCSIDriver)
if wsapi_version:
mock_client.getWsApiVersion.return_value = (
wsapi_version)
else:
mock_client.getWsApiVersion.return_value = (
self.wsapi_version_latest)
expected_get_cpgs = [
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2)]
expected_get_ports = [mock.call.getPorts()]
expected_primera_check = [mock.call.is_primera_array()]
mock_client.assert_has_calls(
self.standard_login +
expected_get_cpgs +
self.standard_logout +
expected_primera_check +
self.standard_login +
expected_get_ports +
self.standard_logout)
mock_client.reset_mock()
return mock_client
def test_iscsi_primera(self):
self.assertRaises(NotImplementedError, self.setup_mock_client,
driver=hpedriver.HPE3PARISCSIDriver,
is_primera=True)
@ddt.data('volume', 'volume_name_id')
def test_initialize_connection(self, volume_attr):
volume = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
[{'hostname': self.FAKE_HOST,
'volumeName': vol_name,
'lun': self.TARGET_LUN,
'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}],
[{'active': True,
'volumeName': vol_name,
'lun': self.TARGET_LUN, 'type': 0}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': vol_name,
'lun_id': self.TARGET_LUN,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
result = self.driver.initialize_connection(
volume,
self.connector)
expected = [
mock.call.getVolume(vol_name),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(self.properties, result)
def test_initialize_connection_multipath(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN, 'type': 0,
'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': self.TARGET_LUN,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
mock_client.getiSCSIPorts.return_value = [{
'IPAddr': '1.1.1.2',
'iSCSIName': self.TARGET_IQN,
}]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = self.volume
volume['replication_status'] = 'disabled'
result = self.driver.initialize_connection(
volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getiSCSIPorts(
state=self.mock_client_conf['PORT_STATE_READY']),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST,
portPos=self.FAKE_ISCSI_PORT['portPos'],
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(self.multipath_properties, result)
def test_initialize_connection_multipath_existing_nsp(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
[{'hostname': self.FAKE_HOST,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN,
'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}],
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN, 'type': 0}]]
mock_client.getiSCSIPorts.return_value = [{
'IPAddr': '1.1.1.2',
'iSCSIName': self.TARGET_IQN,
}]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = self.volume
volume['replication_status'] = 'disabled'
result = self.driver.initialize_connection(
volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getiSCSIPorts(
state=self.mock_client_conf['PORT_STATE_READY']),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(self.multipath_properties, result)
def test_initialize_connection_encrypted(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
[{'hostname': self.FAKE_HOST,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN,
'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}],
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN, 'type': 0}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': self.TARGET_LUN,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
result = self.driver.initialize_connection(
self.volume_encrypted,
self.connector)
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
expected_properties = self.properties
expected_properties['data']['encrypted'] = True
self.assertDictEqual(self.properties, result)
@mock.patch.object(volume_types, 'get_volume_type')
def test_initialize_connection_peer_persistence(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
self.replication_targets[0]['quorum_witness_ip'] = '10.50.3.192'
self.replication_targets[0]['hpe3par_iscsi_ips'] = '1.1.1.2'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'volume_type': self.volume_type_replicated}}
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN, 'type': 0,
'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}]]
mock_replicated_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN, 'type': 0,
'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': self.TARGET_LUN,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
mock_replicated_client.createVLUN.return_value = location
mock_client.getiSCSIPorts.return_value = [{
'IPAddr': '1.1.1.2',
'iSCSIName': self.TARGET_IQN,
}]
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
volume = self.volume
volume['replication_status'] = 'enabled'
result = self.driver.initialize_connection(
volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getiSCSIPorts(state=4),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST,
portPos=self.FAKE_ISCSI_PORT['portPos'],
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(self.multipath_properties, result)
def test_terminate_connection_for_clear_chap_creds_not_found(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getHostVLUNs.return_value = [
{'active': False,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID
}
# Test for clear CHAP creds fails with HTTPNotFound
mock_client.removeVolumeMetaData.side_effect = [
hpeexceptions.HTTPNotFound,
hpeexceptions.HTTPNotFound]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.terminate_connection(
self.volume,
self.connector,
force=True)
expected = [
mock.call.queryHost(iqns=[self.connector['initiator']]),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.modifyHost(
'fakehost',
{'pathOperation': 2,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME, CHAP_USER_KEY),
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME, CHAP_PASS_KEY)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_terminate_connection_for_clear_chap_user_key_bad_request(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getHostVLUNs.return_value = [
{'active': False,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID
}
# Test for CHAP USER KEY fails with HTTPBadRequest
mock_client.removeVolumeMetaData.side_effect = [
hpeexceptions.HTTPBadRequest]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(hpeexceptions.HTTPBadRequest,
self.driver.terminate_connection,
self.volume,
self.connector,
force=True)
expected = [
mock.call.queryHost(iqns=[self.connector['initiator']]),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.modifyHost(
'fakehost',
{'pathOperation': 2,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME, CHAP_USER_KEY)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_terminate_connection_for_clear_chap_pass_key_bad_request(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getHostVLUNs.return_value = [
{'active': False,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
}
# Test for CHAP PASS KEY fails with HTTPBadRequest
mock_client.removeVolumeMetaData.side_effect = [
None,
hpeexceptions.HTTPBadRequest]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.assertRaises(hpeexceptions.HTTPBadRequest,
self.driver.terminate_connection,
self.volume,
self.connector,
force=True)
expected = [
mock.call.queryHost(iqns=[self.connector['initiator']]),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.modifyHost(
'fakehost',
{'pathOperation': 2,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME, CHAP_USER_KEY),
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME, CHAP_PASS_KEY)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_get_volume_stats(self):
# setup_mock_client drive with the configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.filter_function = FILTER_FUNCTION
config.goodness_function = GOODNESS_FUNCTION
mock_client = self.setup_driver(config=config)
mock_client.getCPG.return_value = self.cpgs[0]
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234'
}
# cpg has no limit
mock_client.getCPGAvailableSpace.return_value = {
"capacityEfficiency": {u'compaction': 594.4},
"rawFreeMiB": 1024.0 * 6,
"usableFreeMiB": 1024.0 * 3
}
stat_capabilities = {
THROUGHPUT: 0,
BANDWIDTH: 0,
LATENCY: 0,
IO_SIZE: 0,
QUEUE_LENGTH: 0,
AVG_BUSY_PERC: 0
}
mock_client.getCPGStatData.return_value = stat_capabilities
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
stats = self.driver.get_volume_stats(True)
const = 0.0009765625
self.assertEqual('iSCSI', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertTrue(stats['pools'][0]['thin_provisioning_support'])
self.assertTrue(stats['pools'][0]['thick_provisioning_support'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(86.0,
stats['pools'][0]['provisioned_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertEqual(stat_capabilities[THROUGHPUT],
stats['pools'][0][THROUGHPUT])
self.assertEqual(stat_capabilities[BANDWIDTH],
stats['pools'][0][BANDWIDTH])
self.assertEqual(stat_capabilities[LATENCY],
stats['pools'][0][LATENCY])
self.assertEqual(stat_capabilities[IO_SIZE],
stats['pools'][0][IO_SIZE])
self.assertEqual(stat_capabilities[QUEUE_LENGTH],
stats['pools'][0][QUEUE_LENGTH])
self.assertEqual(stat_capabilities[AVG_BUSY_PERC],
stats['pools'][0][AVG_BUSY_PERC])
expected = [
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPGStatData(HPE3PAR_CPG, 'daily', '7d'),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2),
mock.call.getCPGStatData(HPE3PAR_CPG2, 'daily', '7d'),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
cpg2 = self.cpgs[0].copy()
cpg2.update({'SDGrowth': {'limitMiB': 8192}})
mock_client.getCPG.return_value = cpg2
stats = self.driver.get_volume_stats(True)
self.assertEqual('iSCSI', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertTrue(stats['pools'][0]['thin_provisioning_support'])
self.assertTrue(stats['pools'][0]['thick_provisioning_support'])
total_capacity_gb = 8192 * const
self.assertEqual(total_capacity_gb,
stats['pools'][0]['total_capacity_gb'])
free_capacity_gb = int(
(8192 - (self.cpgs[0]['UsrUsage']['usedMiB'] +
self.cpgs[0]['SDUsage']['usedMiB'])) * const)
self.assertEqual(free_capacity_gb,
stats['pools'][0]['free_capacity_gb'])
cap_util = (float(total_capacity_gb - free_capacity_gb) /
float(total_capacity_gb)) * 100
self.assertEqual(cap_util,
stats['pools'][0]['capacity_utilization'])
provisioned_capacity_gb = int(
(self.cpgs[0]['UsrUsage']['totalMiB'] +
self.cpgs[0]['SAUsage']['totalMiB'] +
self.cpgs[0]['SDUsage']['totalMiB']) * const)
self.assertEqual(provisioned_capacity_gb,
stats['pools'][0]['provisioned_capacity_gb'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertEqual(stat_capabilities[THROUGHPUT],
stats['pools'][0][THROUGHPUT])
self.assertEqual(stat_capabilities[BANDWIDTH],
stats['pools'][0][BANDWIDTH])
self.assertEqual(stat_capabilities[LATENCY],
stats['pools'][0][LATENCY])
self.assertEqual(stat_capabilities[IO_SIZE],
stats['pools'][0][IO_SIZE])
self.assertEqual(stat_capabilities[QUEUE_LENGTH],
stats['pools'][0][QUEUE_LENGTH])
self.assertEqual(stat_capabilities[AVG_BUSY_PERC],
stats['pools'][0][AVG_BUSY_PERC])
def test_get_volume_stats2(self):
# Testing when the API_VERSION is incompatible with getCPGStatData
srstatld_api_version = 30201200
pre_srstatld_api_version = srstatld_api_version - 1
wsapi = {'build': pre_srstatld_api_version}
config = self.setup_configuration()
config.filter_function = FILTER_FUNCTION
config.goodness_function = GOODNESS_FUNCTION
mock_client = self.setup_driver(config=config, wsapi_version=wsapi)
mock_client.getCPG.return_value = self.cpgs[0]
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234'
}
# cpg has no limit
mock_client.getCPGAvailableSpace.return_value = {
"capacityEfficiency": {u'compaction': 594.4},
"rawFreeMiB": 1024.0 * 6,
"usableFreeMiB": 1024.0 * 3
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
stats = self.driver.get_volume_stats(True)
self.assertEqual('iSCSI', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertIsNone(stats['pools'][0][THROUGHPUT])
self.assertIsNone(stats['pools'][0][BANDWIDTH])
self.assertIsNone(stats['pools'][0][LATENCY])
self.assertIsNone(stats['pools'][0][IO_SIZE])
self.assertIsNone(stats['pools'][0][QUEUE_LENGTH])
self.assertIsNone(stats['pools'][0][AVG_BUSY_PERC])
expected = [
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_get_volume_stats3(self):
# Testing when the client version is incompatible with getCPGStatData
# setup_mock_client drive with the configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.filter_function = FILTER_FUNCTION
config.goodness_function = GOODNESS_FUNCTION
mock_client = self.setup_driver(config=config,
wsapi_version=self.wsapi_version_312)
mock_client.getCPG.return_value = self.cpgs[0]
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234'
}
# cpg has no limit
mock_client.getCPGAvailableSpace.return_value = {
"capacityEfficiency": {u'compaction': 594.4},
"rawFreeMiB": 1024.0 * 6,
"usableFreeMiB": 1024.0 * 3
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
stats = self.driver.get_volume_stats(True)
self.assertEqual('iSCSI', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertIsNone(stats['pools'][0][THROUGHPUT])
self.assertIsNone(stats['pools'][0][BANDWIDTH])
self.assertIsNone(stats['pools'][0][LATENCY])
self.assertIsNone(stats['pools'][0][IO_SIZE])
self.assertIsNone(stats['pools'][0][QUEUE_LENGTH])
self.assertIsNone(stats['pools'][0][AVG_BUSY_PERC])
expected = [
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_get_volume_stats4(self):
# Testing get_volume_stats() when System Reporter license is not active
# setup_mock_client drive with the configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.filter_function = FILTER_FUNCTION
config.goodness_function = GOODNESS_FUNCTION
mock_client = self.setup_driver(config=config)
mock_client.getCPG.return_value = self.cpgs[0]
# Purposely left out the System Reporter license in
# getStorageSystemInfo to test sr_support
mock_client.getStorageSystemInfo.return_value = {
'id': self.CLIENT_ID,
'serialNumber': '1234',
'licenseInfo': {
'licenses': [{'name': 'Remote Copy'},
{'name': 'Priority Optimization'},
{'name': 'Thin Provisioning'}]
}
}
# cpg has no limit
mock_client.getCPGAvailableSpace.return_value = {
"capacityEfficiency": {u'compaction': 594.4},
"rawFreeMiB": 1024.0 * 6,
"usableFreeMiB": 1024.0 * 3
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
stats = self.driver.get_volume_stats(True)
self.assertEqual('iSCSI', stats['storage_protocol'])
self.assertEqual('12345', stats['array_id'])
self.assertTrue(stats['pools'][0]['thin_provisioning_support'])
self.assertTrue(stats['pools'][0]['QoS_support'])
self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb'])
self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb'])
self.assertEqual(87.5, stats['pools'][0]['capacity_utilization'])
self.assertEqual(3, stats['pools'][0]['total_volumes'])
self.assertEqual(GOODNESS_FUNCTION,
stats['pools'][0]['goodness_function'])
self.assertEqual(FILTER_FUNCTION,
stats['pools'][0]['filter_function'])
self.assertIsNone(stats['pools'][0][THROUGHPUT])
self.assertIsNone(stats['pools'][0][BANDWIDTH])
self.assertIsNone(stats['pools'][0][LATENCY])
self.assertIsNone(stats['pools'][0][IO_SIZE])
self.assertIsNone(stats['pools'][0][QUEUE_LENGTH])
self.assertIsNone(stats['pools'][0][AVG_BUSY_PERC])
expected = [
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG),
mock.call.getCPG(HPE3PAR_CPG2),
mock.call.getCPGAvailableSpace(HPE3PAR_CPG2)]
mock_client.assert_has_calls(
self.get_id_login +
self.standard_logout +
self.standard_login +
expected +
self.standard_logout)
def test_create_host_with_unmanage_iscsi_and_manage_fc_hosts(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
def get_side_effect(*args):
host = {'name': None}
if args[0] == 'fake':
host['name'] = 'fake'
elif args[0] == self.FAKE_HOST:
host['name'] = self.FAKE_HOST
host['iSCSIPaths'] = [{
"name": "iqn.1993-08.org.debian:01:222"}]
return host
mock_client.getHost.side_effect = get_side_effect
mock_client.queryHost.return_value = {
'members': [{
'name': 'fake'
}]
}
mock_client.getVLUN.return_value = {'lun': 186}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=[self.connector['initiator']]),
mock.call.getHost('fake')]
mock_client.assert_has_calls(expected)
self.assertEqual('fake', host['name'])
def test_create_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.queryHost.return_value = None
mock_client.getVLUN.return_value = {'lun': self.TARGET_LUN}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.createHost(
self.FAKE_HOST,
optional={'domain': None, 'persona': 2},
iscsiNames=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
self.assertIsNone(auth_username)
self.assertIsNone(auth_password)
self.assertEqual(HPE3PAR_CPG, cpg)
def test_create_host_chap_enabled(self):
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.queryHost.return_value = None
mock_client.getVLUN.return_value = {'lun': self.TARGET_LUN}
expected_mod_request = {
'chapOperation': mock_client.HOST_EDIT_ADD,
'chapOperationMode': mock_client.CHAP_INITIATOR,
'chapName': 'test-user',
'chapSecret': 'test-pass'
}
def get_side_effect(*args):
data = {'value': None}
if args[1] == CHAP_USER_KEY:
data['value'] = 'test-user'
elif args[1] == CHAP_PASS_KEY:
data['value'] = 'test-pass'
return data
mock_client.getVolumeMetaData.side_effect = get_side_effect
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.createHost(
self.FAKE_HOST,
optional={'domain': None, 'persona': 2},
iscsiNames=['iqn.1993-08.org.debian:01:222']),
mock.call.modifyHost(
'fakehost',
expected_mod_request),
mock.call.getHost(self.FAKE_HOST)
]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
self.assertEqual('test-user', auth_username)
self.assertEqual('test-pass', auth_password)
def test_create_host_chap_enabled_and_host_with_chap_cred(self):
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.return_value = {
'name': self.FAKE_HOST,
'initiatorChapEnabled': True,
'iSCSIPaths': [{
"name": "iqn.1993-08.org.debian:01:222"
}]
}
mock_client.queryHost.return_value = None
mock_client.getVLUN.return_value = {'lun': self.TARGET_LUN}
expected_mod_request = {
'chapOperation': mock_client.HOST_EDIT_ADD,
'chapOperationMode': mock_client.CHAP_INITIATOR,
'chapName': 'test-user',
'chapSecret': 'test-pass'
}
def get_side_effect(*args):
data = {'value': None}
if args[1] == CHAP_USER_KEY:
data['value'] = 'test-user'
elif args[1] == CHAP_PASS_KEY:
data['value'] = 'test-pass'
return data
mock_client.getVolumeMetaData.side_effect = get_side_effect
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.modifyHost(
'fakehost',
expected_mod_request),
mock.call.getHost(self.FAKE_HOST)
]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
self.assertEqual('test-user', auth_username)
self.assertEqual('test-pass', auth_password)
def test_create_host_chap_enabled_and_host_without_chap_cred(self):
# setup_mock_client driver
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.queryHost.return_value = None
expected_mod_request = {
'chapOperation': mock_client.HOST_EDIT_ADD,
'chapOperationMode': mock_client.CHAP_INITIATOR,
'chapName': 'test-user',
'chapSecret': 'test-pass'
}
def get_side_effect(*args):
data = {'value': None}
if args[1] == CHAP_USER_KEY:
data['value'] = 'test-user'
elif args[1] == CHAP_PASS_KEY:
data['value'] = 'test-pass'
return data
mock_client.getVolumeMetaData.side_effect = get_side_effect
mock_client.getHost.return_value = {
'name': self.FAKE_HOST,
'initiatorChapEnabled': False,
'iSCSIPaths': [{
"name": "iqn.1993-08.org.debian:01:222"
}]
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.modifyHost(self.FAKE_HOST, expected_mod_request)]
mock_client.assert_has_calls(expected)
def test_create_invalid_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('Host not found.'),
{'name': 'fakehost.foo'}]
mock_client.queryHost.return_value = {
'members': [{
'name': 'fakehost.foo'
}]
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost('fakehost.foo')]
mock_client.assert_has_calls(expected)
self.assertEqual('fakehost.foo', host['name'])
self.assertIsNone(auth_username)
self.assertIsNone(auth_password)
def test_create_invalid_host_chap_enabled(self):
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('Host not found.'),
{'name': 'fakehost.foo'}]
mock_client.queryHost.return_value = {
'members': [{
'name': 'fakehost.foo'
}]
}
def get_side_effect(*args):
data = {'value': None}
if args[1] == CHAP_USER_KEY:
data['value'] = 'test-user'
elif args[1] == CHAP_PASS_KEY:
data['value'] = 'test-pass'
return data
mock_client.getVolumeMetaData.side_effect = get_side_effect
expected_mod_request = {
'chapOperation': mock_client.HOST_EDIT_ADD,
'chapOperationMode': mock_client.CHAP_INITIATOR,
'chapName': 'test-user',
'chapSecret': 'test-pass'
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.modifyHost(
'fakehost.foo',
expected_mod_request),
mock.call.getHost('fakehost.foo')
]
mock_client.assert_has_calls(expected)
self.assertEqual('fakehost.foo', host['name'])
self.assertEqual('test-user', auth_username)
self.assertEqual('test-pass', auth_password)
def test_create_host_concurrent(self):
# tests concurrent requests of create host
# setup_mock_client driver with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.queryHost.side_effect = [
None, {'members': [{'name': self.FAKE_HOST}]}]
mock_client.createHost.side_effect = [
hpeexceptions.HTTPConflict(
{'code': EXISTENT_PATH,
'desc': 'host WWN/iSCSI name already used by another host'})]
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, user, pwd, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.createHost(
self.FAKE_HOST,
optional={'domain': None, 'persona': 2},
iscsiNames=['iqn.1993-08.org.debian:01:222']),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
def test_create_modify_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
{'name': self.FAKE_HOST, 'FCPaths': []},
{'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}]
mock_client.queryHost.return_value = None
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.modifyHost(
self.FAKE_HOST,
{'pathOperation': 1,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
self.assertIsNone(auth_username)
self.assertIsNone(auth_password)
self.assertEqual(2, len(host['FCPaths']))
def test_create_modify_host_chap_enabled(self):
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
{'name': self.FAKE_HOST, 'FCPaths': []},
{'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}]
mock_client.queryHost.return_value = None
def get_side_effect(*args):
data = {'value': None}
if args[1] == CHAP_USER_KEY:
data['value'] = 'test-user'
elif args[1] == CHAP_PASS_KEY:
data['value'] = 'test-pass'
return data
mock_client.getVolumeMetaData.side_effect = get_side_effect
expected_mod_request = {
'chapOperation': mock_client.HOST_EDIT_ADD,
'chapOperationMode': mock_client.CHAP_INITIATOR,
'chapName': 'test-user',
'chapSecret': 'test-pass'
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
host, auth_username, auth_password, cpg = self.driver._create_host(
common, self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.modifyHost(
self.FAKE_HOST,
{'pathOperation': 1,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.modifyHost(
self.FAKE_HOST,
expected_mod_request
),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(self.FAKE_HOST, host['name'])
self.assertEqual('test-user', auth_username)
self.assertEqual('test-pass', auth_password)
self.assertEqual(2, len(host['FCPaths']))
def test_get_least_used_nsp_for_host_single(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
# Setup two ISCSI IPs
conf = self.setup_configuration()
conf.hpe3par_iscsi_ips = ["10.10.220.253"]
mock_client = self.setup_driver(config=conf)
mock_client.getPorts.return_value = PORTS_RET
mock_client.getVLUNs.return_value = VLUNS1_RET
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.driver.initialize_iscsi_ports(common)
nsp = self.driver._get_least_used_nsp_for_host(common, 'newhost')
self.assertEqual("1:8:1", nsp)
def test_get_least_used_nsp_for_host_new(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
# Setup two ISCSI IPs
conf = self.setup_configuration()
conf.hpe3par_iscsi_ips = ["10.10.220.252", "10.10.220.253"]
mock_client = self.setup_driver(config=conf)
mock_client.getPorts.return_value = PORTS_RET
mock_client.getVLUNs.return_value = VLUNS1_RET
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.driver.initialize_iscsi_ports(common)
# Host 'newhost' does not yet have any iscsi paths,
# so the 'least used' is returned
nsp = self.driver._get_least_used_nsp_for_host(common, 'newhost')
self.assertEqual("1:8:2", nsp)
def test_get_least_used_nsp_for_host_reuse(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
# Setup two ISCSI IPs
conf = self.setup_configuration()
conf.hpe3par_iscsi_ips = ["10.10.220.252", "10.10.220.253"]
mock_client = self.setup_driver(config=conf)
mock_client.getPorts.return_value = PORTS_RET
mock_client.getVLUNs.return_value = VLUNS1_RET
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.driver.initialize_iscsi_ports(common)
# hosts 'foo' and 'bar' already have active iscsi paths
# the same one should be used
nsp = self.driver._get_least_used_nsp_for_host(common, 'foo')
self.assertEqual("1:8:2", nsp)
nsp = self.driver._get_least_used_nsp_for_host(common, 'bar')
self.assertEqual("1:8:1", nsp)
def test_get_least_used_nps_for_host_fc(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getPorts.return_value = PORTS1_RET
mock_client.getVLUNs.return_value = VLUNS5_RET
# Setup two ISCSI IPs
iscsi_ips = ["10.10.220.252", "10.10.220.253"]
self.driver.configuration.hpe3par_iscsi_ips = iscsi_ips
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.driver.initialize_iscsi_ports(common)
nsp = self.driver._get_least_used_nsp_for_host(common, 'newhost')
self.assertNotEqual("0:6:3", nsp)
self.assertEqual("1:8:1", nsp)
def test_invalid_iscsi_ip(self):
config = self.setup_configuration()
config.hpe3par_iscsi_ips = ['10.10.220.250', '10.10.220.251']
config.target_ip_address = '10.10.10.10'
mock_conf = {
'getPorts.return_value': {
'members': [
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.220.252',
'linkState': 4,
'device': [],
'iSCSIName': self.TARGET_IQN,
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': self.TARGET_IQN,
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8}]}}
# no valid ip addr should be configured.
self.assertRaises(exception.InvalidInput,
self.setup_driver,
config=config,
mock_conf=mock_conf)
def test_get_least_used_nsp(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
ports = [
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2}, 'active': True},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 2}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}]
mock_client.getVLUNs.return_value = {'members': ports}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
# in use count
vluns = common.client.getVLUNs()
nsp = self.driver._get_least_used_nsp(common, vluns['members'],
['0:2:1', '1:8:1'])
self.assertEqual('1:8:1', nsp)
ports = [
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True}]
mock_client.getVLUNs.return_value = {'members': ports}
# in use count
common = self.driver._login()
vluns = common.client.getVLUNs()
nsp = self.driver._get_least_used_nsp(common, vluns['members'],
['0:2:1', '1:2:1'])
self.assertEqual('1:2:1', nsp)
ports = [
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1},
'active': True}]
mock_client.getVLUNs.return_value = {'members': ports}
# in use count
common = self.driver._login()
vluns = common.client.getVLUNs()
nsp = self.driver._get_least_used_nsp(common, vluns['members'],
['1:1:1', '1:2:1'])
self.assertEqual('1:1:1', nsp)
def test_set_3par_chaps(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
expected = []
self.driver._set_3par_chaps(
common, 'test-host', 'test-vol', 'test-host', 'pass')
mock_client.assert_has_calls(expected)
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
expected_mod_request = {
'chapOperation': mock_client.HOST_EDIT_ADD,
'chapOperationMode': mock_client.CHAP_INITIATOR,
'chapName': 'test-host',
'chapSecret': 'fake'
}
expected = [
mock.call.modifyHost('test-host', expected_mod_request)
]
self.driver._set_3par_chaps(
common, 'test-host', 'test-vol', 'test-host', 'fake')
mock_client.assert_has_calls(expected)
@mock.patch('cinder.volume.volume_utils.generate_password')
def test_do_export(self, mock_utils):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
volume = {'host': 'test-host@3pariscsi',
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
connector = {'host': 'test-host'}
mock_utils.return_value = 'random-pass'
mock_client.getHostVLUNs.return_value = [
{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0,
'remoteName': 'iqn.1993-08.org.debian:01:222'}
]
mock_client.getHost.return_value = {
'name': 'osv-0DM4qZEVSKON-DXN-NwVpw',
'initiatorChapEnabled': True
}
mock_client.getVolumeMetaData.return_value = {
'value': 'random-pass'
}
expected = []
expected_model = {'provider_auth': None}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
model = self.driver._do_export(common, volume, connector)
mock_client.assert_has_calls(expected)
self.assertEqual(expected_model, model)
mock_client.reset_mock()
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
volume = {'host': 'test-host@3pariscsi',
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
mock_utils.return_value = 'random-pass'
mock_client.getHostVLUNs.return_value = [
{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0,
'remoteName': 'iqn.1993-08.org.debian:01:222'}
]
mock_client.getHost.return_value = {
'name': 'osv-0DM4qZEVSKON-DXN-NwVpw',
'initiatorChapEnabled': True
}
mock_client.getVolumeMetaData.return_value = {
'value': 'random-pass'
}
expected = [
mock.call.getHostVLUNs('test-host'),
mock.call.getHost('test-host'),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')
]
expected_model = {'provider_auth': 'CHAP test-host random-pass'}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
model = self.driver._do_export(common, volume, connector)
mock_client.assert_has_calls(expected)
self.assertEqual(expected_model, model)
@mock.patch('cinder.volume.volume_utils.generate_password')
def test_do_export_host_not_found(self, mock_utils):
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
volume = {'host': 'test-host@3pariscsi',
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
connector = {'host': 'test-host'}
mock_utils.return_value = "random-pass"
mock_client.getHostVLUNs.side_effect = hpeexceptions.HTTPNotFound(
'fake')
mock_client.getVolumeMetaData.return_value = {
'value': 'random-pass'
}
expected = [
mock.call.getHostVLUNs('test-host'),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')
]
expected_model = {'provider_auth': 'CHAP test-host random-pass'}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
model = self.driver._do_export(common, volume, connector)
mock_client.assert_has_calls(expected)
self.assertEqual(expected_model, model)
@mock.patch('cinder.volume.volume_utils.generate_password')
def test_do_export_host_chap_disabled(self, mock_utils):
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
volume = {'host': 'test-host@3pariscsi',
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
connector = {'host': 'test-host'}
mock_utils.return_value = 'random-pass'
mock_client.getHostVLUNs.return_value = [
{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0,
'remoteName': 'iqn.1993-08.org.debian:01:222'}
]
mock_client.getHost.return_value = {
'name': 'fake-host',
'initiatorChapEnabled': False
}
mock_client.getVolumeMetaData.return_value = {
'value': 'random-pass'
}
expected = [
mock.call.getHostVLUNs('test-host'),
mock.call.getHost('test-host'),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')
]
expected_model = {'provider_auth': 'CHAP test-host random-pass'}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
model = self.driver._do_export(common, volume, connector)
mock_client.assert_has_calls(expected)
self.assertEqual(expected_model, model)
@mock.patch('cinder.volume.volume_utils.generate_password')
def test_do_export_no_active_vluns(self, mock_utils):
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
volume = {'host': 'test-host@3pariscsi',
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
connector = {'host': 'test-host'}
mock_utils.return_value = "random-pass"
mock_client.getHostVLUNs.return_value = [
{'active': False,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0,
'remoteName': 'iqn.1993-08.org.debian:01:222'}
]
mock_client.getHost.return_value = {
'name': 'fake-host',
'initiatorChapEnabled': True
}
mock_client.getVolumeMetaData.return_value = {
'value': 'random-pass'
}
expected = [
mock.call.getHostVLUNs('test-host'),
mock.call.getHost('test-host'),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')
]
expected_model = {'provider_auth': 'CHAP test-host random-pass'}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
model = self.driver._do_export(common, volume, connector)
mock_client.assert_has_calls(expected)
self.assertEqual(expected_model, model)
@mock.patch('cinder.volume.volume_utils.generate_password')
def test_do_export_vlun_missing_chap_credentials(self, mock_utils):
# setup_mock_client drive with CHAP enabled configuration
# and return the mock HTTP 3PAR client
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
volume = {'host': 'test-host@3pariscsi',
'id': self.VOLUME_ID}
connector = {'host': 'test-host'}
mock_utils.return_value = 'random-pass'
mock_client.getHost.return_value = {
'name': 'osv-0DM4qZEVSKON-DXN-NwVpw',
'initiatorChapEnabled': True}
mock_client.getVolumeMetaData.side_effect = hpeexceptions.HTTPNotFound
expected = [
mock.call.getHostVLUNs('test-host'),
mock.call.getHost('test-host'),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')]
expected_model = {'provider_auth': 'CHAP test-host random-pass'}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
# vlun has remoteName
mock_client.getHostVLUNs.return_value = [
{'active': True, 'volumeName': self.VOLUME_3PAR_NAME,
'lun': 1, 'type': 3,
'remoteName': 'iqn.1993-08.org.debian:01:222'}]
model_with_remote_name = self.driver._do_export(
common, volume, connector)
mock_client.assert_has_calls(expected)
self.assertDictEqual(expected_model, model_with_remote_name)
# vlun does not has remoteName
mock_client.getHostVLUNs.return_value = [
{'active': False, 'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 1}]
model_without_remote_name = self.driver._do_export(
common, volume, connector)
mock_client.assert_has_calls(expected)
self.assertDictEqual(expected_model, model_without_remote_name)
@mock.patch('cinder.volume.volume_utils.generate_password')
def test_create_export(self, mock_utils):
config = self.setup_configuration()
config.hpe3par_iscsi_chap_enabled = True
mock_client = self.setup_driver(config=config)
mock_utils.return_value = 'random-pass'
volume = {'host': 'test-host@3pariscsi',
'id': self.VOLUME_ID}
connector = {'host': 'test-host'}
mock_client.getHostVLUNs.return_value = [
{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 3,
'remoteName': 'iqn.1993-08.org.debian:01:222'}]
mock_client.getHost.return_value = {
'name': 'osv-0DM4qZEVSKON-DXN-NwVpw',
'initiatorChapEnabled': True}
mock_client.getVolumeMetaData.return_value = {
'value': 'random-pass'}
expected = [
mock.call.getHostVLUNs('test-host'),
mock.call.getHost('test-host'),
mock.call.getVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY, 'test-host'),
mock.call.setVolumeMetaData(
'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY, 'random-pass')]
expected_model = {'provider_auth': 'CHAP test-host random-pass'}
mock_create_client = self.mock_object(hpecommon.HPE3PARCommon,
'_create_client',
return_value=mock_client)
mock_create_client.return_value = mock_client
model = self.driver.create_export(None, volume, connector)
mock_client.assert_has_calls(expected)
self.assertDictEqual(expected_model, model)
def test_initialize_iscsi_ports_with_iscsi_ip_and_port(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
conf.hpe3par_iscsi_ips = ["10.10.220.252:1234"]
mock_client = self.setup_driver(config=conf)
mock_client.getPorts.return_value = PORTS_RET
expected = [mock.call.getPorts()]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.driver.initialize_iscsi_ports(common)
mock_client.assert_has_calls(expected)
def test_initialize_iscsi_ports_with_wrong_ip_format_configured(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
conf.hpe3par_iscsi_ips = ["10.10.220.252:1234:4567"]
mock_client = self.setup_driver(config=conf)
mock_client.getPorts.return_value = PORTS_RET
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.assertRaises(exception.InvalidInput,
self.driver.initialize_iscsi_ports,
common)
def test_ensure_export(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
volume = {'host': 'test-host@3pariscsi',
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
mock_client.getAllVolumeMetaData.return_value = {
'total': 0,
'members': []
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
model = self.driver.ensure_export(None, volume)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getAllVolumeMetaData('osv-0DM4qZEVSKON-DXN-NwVpw')
]
expected_model = {'provider_auth': None}
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_model, model)
mock_client.getAllVolumeMetaData.return_value = {
'total': 2,
'members': [
{
'creationTimeSec': 1406074222,
'value': 'fake-host',
'key': CHAP_USER_KEY,
'creationTime8601': '2014-07-22T17:10:22-07:00'
},
{
'creationTimeSec': 1406074222,
'value': 'random-pass',
'key': CHAP_PASS_KEY,
'creationTime8601': '2014-07-22T17:10:22-07:00'
}
]
}
model = self.driver.ensure_export(None, volume)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getAllVolumeMetaData('osv-0DM4qZEVSKON-DXN-NwVpw')
]
expected_model = {'provider_auth': "CHAP fake-host random-pass"}
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_model, model)
def test_ensure_export_missing_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
volume = {'host': 'test-host@3pariscsi',
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
mock_client.getVolume.side_effect = hpeexceptions.HTTPNotFound(
'fake')
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
model = self.driver.ensure_export(None, volume)
expected = [mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw')]
expected_model = None
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertEqual(expected_model, model)
@mock.patch.object(volume_types, 'get_volume_type')
def test_get_volume_settings_default_pool(self, _mock_volume_types):
_mock_volume_types.return_value = {
'name': 'gold',
'id': 'gold-id',
'extra_specs': {}}
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
volume = {'host': 'test-host@3pariscsi#pool_foo',
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
pool = volume_utils.extract_host(volume['host'], 'pool')
model = common.get_volume_settings_from_type_id('gold-id', pool)
self.assertEqual('pool_foo', model['cpg'])
def test_get_model_update(self):
mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
model_update = common._get_model_update('xxx@yyy#zzz', 'CPG')
self.assertEqual({'host': 'xxx@yyy#CPG'}, model_update)
def test_migrate_volume_attached(self):
self.migrate_volume_attached()
def test_terminate_connection_multiattach(self):
ctx = context.get_admin_context()
mock_client = self.setup_driver()
att_1 = fake_volume.volume_attachment_ovo(
ctx, id=uuidutils.generate_uuid())
att_2 = fake_volume.volume_attachment_ovo(
ctx, id=uuidutils.generate_uuid())
volume = fake_volume.fake_volume_obj(
ctx, multiattach=True, host=self.FAKE_CINDER_HOST)
volume.volume_attachment.objects = [att_1, att_2]
with mock.patch.object(hpecommon.HPE3PARCommon, '_create_client'
) as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.terminate_connection(volume, self.connector)
# When volume is having mulitple instances attached, there
# should be no call to delete the VLUN(s) or the host. We
# can assert these methods were not called to make sure the
# proper code execution is followed.
self.assertEqual(0, mock_client.deleteVLUN.call_count)
self.assertEqual(0, mock_client.deleteHost.call_count)
@ddt.data('volume', 'volume_name_id')
def test_terminate_connection(self, volume_attr):
volume = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getHostVLUNs.return_value = [
{'active': False,
'volumeName': vol_name,
'lun': None, 'type': 0}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.terminate_connection(
volume,
self.connector,
force=True)
expected = [
mock.call.queryHost(iqns=[self.connector['initiator']]),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
vol_name,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.modifyHost(
'fakehost',
{'pathOperation': 2,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.removeVolumeMetaData(vol_name, CHAP_USER_KEY),
mock.call.removeVolumeMetaData(vol_name, CHAP_PASS_KEY)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@mock.patch.object(volume_types, 'get_volume_type')
def test_terminate_connection_peer_persistence(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
self.replication_targets[0]['replication_mode'] = 'sync'
self.replication_targets[0]['quorum_witness_ip'] = '10.50.3.192'
conf.replication_device = self.replication_targets
mock_client = self.setup_driver(config=conf)
mock_client.getStorageSystemInfo.return_value = (
{'id': self.CLIENT_ID})
mock_replicated_client = self.setup_driver(config=conf)
mock_replicated_client.getStorageSystemInfo.return_value = (
{'id': self.REPLICATION_CLIENT_ID})
_mock_volume_types.return_value = {
'name': 'replicated',
'extra_specs': {
'replication_enabled': '<is> True',
'replication:mode': 'sync',
'volume_type': self.volume_type_replicated}}
mock_client.getHostVLUNs.return_value = [
{'active': False,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
volume = self.volume
volume['replication_status'] = 'enabled'
with mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client, \
mock.patch.object(
hpecommon.HPE3PARCommon,
'_create_replication_client') as mock_replication_client:
mock_create_client.return_value = mock_client
mock_replication_client.return_value = mock_replicated_client
self.driver.terminate_connection(
volume,
self.connector_multipath_enabled)
expected = [
mock.call.queryHost(iqns=[self.connector['initiator']]),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.modifyHost(
'fakehost',
{'pathOperation': 2,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME, CHAP_USER_KEY),
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME, CHAP_PASS_KEY)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
VLUNS5_RET = ({'members':
[{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
'active': True},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'active': True}]})
PORTS_RET = ({'members':
[{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.220.252',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21820002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8}]})
VLUNS1_RET = ({'members':
[{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
'hostname': 'foo', 'active': True},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'hostname': 'bar', 'active': True},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'hostname': 'bar', 'active': True},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'hostname': 'bar', 'active': True}]})
PORTS1_RET = ({'members':
[{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.120.252',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21820002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8},
{'portWWN': '20210002AC00383D',
'protocol': 1,
'linkState': 4,
'mode': 2,
'device': ['cage2'],
'nodeWWN': '20210002AC00383D',
'type': 2,
'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]})