cinder/cinder/tests/test_hp3par.py

1691 lines
66 KiB
Python

#
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Unit tests for OpenStack Cinder volume drivers."""
import mock
from hp3parclient import client
from hp3parclient import exceptions as hpexceptions
from cinder import context
from cinder import exception
from cinder.openstack.common import log as logging
from cinder import test
from cinder import units
from cinder.volume.drivers.san.hp import hp_3par_fc as hpfcdriver
from cinder.volume.drivers.san.hp import hp_3par_iscsi as hpdriver
from cinder.volume import qos_specs
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
HP3PAR_CPG = 'OpenStackCPG'
HP3PAR_CPG_SNAP = 'OpenStackCPGSnap'
HP3PAR_USER_NAME = 'testUser'
HP3PAR_USER_PASS = 'testPassword'
HP3PAR_SAN_IP = '2.2.2.2'
HP3PAR_SAN_SSH_PORT = 999
HP3PAR_SAN_SSH_CON_TIMEOUT = 44
HP3PAR_SAN_SSH_PRIVATE = 'foobar'
class HP3PARBaseDriver(object):
VOLUME_ID = 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'
CLONE_ID = 'd03338a9-9115-48a3-8dfc-000000000000'
VOLUME_NAME = 'volume-' + VOLUME_ID
VOLUME_NAME_3PAR = 'osv-0DM4qZEVSKON-DXN-NwVpw'
SNAPSHOT_ID = '2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
SNAPSHOT_NAME = 'snapshot-2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
VOLUME_3PAR_NAME = 'osv-0DM4qZEVSKON-DXN-NwVpw'
SNAPSHOT_3PAR_NAME = 'oss-L4I73ONuTci9Fd4ceij-MQ'
FAKE_HOST = 'fakehost'
USER_ID = '2689d9a913974c008b1d859013f23607'
PROJECT_ID = 'fac88235b9d64685a3530f73e490348f'
VOLUME_ID_SNAP = '761fc5e5-5191-4ec7-aeba-33e36de44156'
FAKE_DESC = 'test description name'
FAKE_FC_PORTS = [{'portPos': {'node': 7, 'slot': 1, 'cardPort': 1},
'portWWN': '0987654321234',
'protocol': 1,
'mode': 2,
'linkState': 4},
{'portPos': {'node': 6, 'slot': 1, 'cardPort': 1},
'portWWN': '123456789000987',
'protocol': 1,
'mode': 2,
'linkState': 4}]
QOS = {'qos:maxIOPS': '1000', 'qos:maxBWS': '50',
'qos:minIOPS': '100', 'qos:minBWS': '25',
'qos:latency': '25', 'qos:priority': 'low'}
QOS_SPECS = {'maxIOPS': '1000', 'maxBWS': '50',
'minIOPS': '100', 'minBWS': '25',
'latency': '25', 'priority': 'low'}
VVS_NAME = "myvvs"
FAKE_ISCSI_PORT = {'portPos': {'node': 8, 'slot': 1, 'cardPort': 1},
'protocol': 2,
'mode': 2,
'IPAddr': '1.1.1.2',
'iSCSIName': ('iqn.2000-05.com.3pardata:'
'21810002ac00383d'),
'linkState': 4}
volume = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_HOST,
'volume_type': None,
'volume_type_id': None}
volume_qos = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_HOST,
'volume_type': None,
'volume_type_id': 'gold'}
snapshot = {'name': SNAPSHOT_NAME,
'id': SNAPSHOT_ID,
'user_id': USER_ID,
'project_id': PROJECT_ID,
'volume_id': VOLUME_ID_SNAP,
'volume_name': VOLUME_NAME,
'status': 'creating',
'progress': '0%',
'volume_size': 2,
'display_name': 'fakesnap',
'display_description': FAKE_DESC}
wwn = ["123456789012345", "123456789054321"]
connector = {'ip': '10.0.0.2',
'initiator': 'iqn.1993-08.org.debian:01:222',
'wwpns': [wwn[0], wwn[1]],
'wwnns': ["223456789012345", "223456789054321"],
'host': FAKE_HOST}
volume_type = {'name': 'gold',
'deleted': False,
'updated_at': None,
'extra_specs': {'qos:maxIOPS': '1000',
'qos:maxBWS': '50',
'qos:minIOPS': '100',
'qos:minBWS': '25',
'qos:latency': '25',
'qos:priority': 'low'},
'deleted_at': None,
'id': 'gold'}
cpgs = [
{'SAGrowth': {'LDLayout': {'diskPatterns': [{'diskType': 2}]},
'incrementMiB': 8192},
'SAUsage': {'rawTotalMiB': 24576,
'rawUsedMiB': 768,
'totalMiB': 8192,
'usedMiB': 256},
'SDGrowth': {'LDLayout': {'RAIDType': 4,
'diskPatterns': [{'diskType': 2}]},
'incrementMiB': 32768},
'SDUsage': {'rawTotalMiB': 49152,
'rawUsedMiB': 1023,
'totalMiB': 36864,
'usedMiB': 768},
'UsrUsage': {'rawTotalMiB': 57344,
'rawUsedMiB': 43349,
'totalMiB': 43008,
'usedMiB': 32512},
'additionalStates': [],
'degradedStates': [],
'failedStates': [],
'id': 5,
'name': HP3PAR_CPG,
'numFPVVs': 2,
'numTPVVs': 0,
'state': 1,
'uuid': '29c214aa-62b9-41c8-b198-543f6cf24edf'}]
def setup_configuration(self):
configuration = mock.Mock()
configuration.hp3par_debug = False
configuration.hp3par_username = HP3PAR_USER_NAME
configuration.hp3par_password = HP3PAR_USER_PASS
configuration.hp3par_api_url = 'https://1.1.1.1/api/v1'
configuration.hp3par_cpg = HP3PAR_CPG
configuration.hp3par_cpg_snap = HP3PAR_CPG_SNAP
configuration.iscsi_ip_address = '1.1.1.2'
configuration.iscsi_port = '1234'
configuration.san_ip = HP3PAR_SAN_IP
configuration.san_login = HP3PAR_USER_NAME
configuration.san_password = HP3PAR_USER_PASS
configuration.san_ssh_port = HP3PAR_SAN_SSH_PORT
configuration.ssh_conn_timeout = HP3PAR_SAN_SSH_CON_TIMEOUT
configuration.san_private_key = HP3PAR_SAN_SSH_PRIVATE
configuration.hp3par_snapshot_expiration = ""
configuration.hp3par_snapshot_retention = ""
configuration.hp3par_iscsi_ips = []
return configuration
@mock.patch(
'hp3parclient.client.HP3ParClient',
spec=True,
PORT_MODE_TARGET=client.HP3ParClient.PORT_MODE_TARGET,
PORT_STATE_READY=client.HP3ParClient.PORT_STATE_READY,
PORT_PROTO_ISCSI=client.HP3ParClient.PORT_PROTO_ISCSI,
PORT_PROTO_FC=client.HP3ParClient.PORT_PROTO_FC,
TASK_DONE=client.HP3ParClient.TASK_DONE,
HOST_EDIT_ADD=client.HP3ParClient.HOST_EDIT_ADD)
def setup_mock_client(self, _m_client, driver, conf=None, m_conf=None):
_m_client = _m_client.return_value
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
def test_create_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.create_volume(self.volume)
comment = (
'{"display_name": "Foo Volume", "type": "OpenStack",'
' "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",'
' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HP3PAR_CPG,
1907, {
'comment': comment,
'tpvv': True,
'snapCPG': HP3PAR_CPG_SNAP}),
mock.call.logout()]
mock_client.assert_has_calls(expected)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_qos(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'cpg': HP3PAR_CPG,
'snap_cpg': HP3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
'volume_type': self.volume_type}}
self.driver.create_volume(self.volume_qos)
comment = (
'{"volume_type_name": "gold", "display_name": "Foo Volume"'
', "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7'
'", "volume_type_id": "gold", "volume_id": "d03338a9-91'
'15-48a3-8dfc-35cdfcdc15a7", "qos": {}, "type": "OpenStack"}')
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.createVolume(
self.VOLUME_3PAR_NAME,
HP3PAR_CPG,
1907, {
'comment': comment,
'tpvv': True,
'snapCPG': HP3PAR_CPG_SNAP}),
mock.call.logout()]
mock_client.assert_has_calls(expected)
def test_delete_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.delete_volume(self.volume)
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.deleteVolume(self.VOLUME_3PAR_NAME),
mock.call.logout()]
mock_client.assert_has_calls(expected)
def test_create_cloned_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.copyVolume.return_value = {'taskid': 1}
volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
'id': HP3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': HP3PARBaseDriver.FAKE_HOST,
'source_volid': HP3PARBaseDriver.VOLUME_ID}
src_vref = {}
model_update = self.driver.create_cloned_volume(volume, src_vref)
self.assertIsNotNone(model_update)
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.copyVolume(
self.VOLUME_3PAR_NAME,
'osv-0DM4qZEVSKON-AAAAAAAAA',
HP3PAR_CPG,
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
'online': True}),
mock.call.logout()]
mock_client.assert_has_calls(expected)
def test_migrate_volume(self):
conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
'getStorageSystemInfo.return_value': {
'serialNumber': '1234'},
'getTask.return_value': {
'status': 1},
'getCPG.return_value': {},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {}
}
mock_client = self.setup_driver(mock_conf=conf)
volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
'id': HP3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'status': 'available',
'host': HP3PARBaseDriver.FAKE_HOST,
'source_volid': HP3PARBaseDriver.VOLUME_ID}
volume_name_3par = self.driver.common._encode_name(volume['id'])
loc_info = 'HP3PARDriver:1234:CPG-FC1'
host = {'host': 'stack@3parfc1',
'capabilities': {'location_info': loc_info}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((True, None), result)
osv_matcher = 'osv-' + volume_name_3par
omv_matcher = 'omv-' + volume_name_3par
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getStorageSystemInfo(),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getCPG('CPG-FC1'),
mock.call.copyVolume(osv_matcher, omv_matcher, mock.ANY, mock.ANY),
mock.call.getTask(mock.ANY),
mock.call.getVolume(osv_matcher),
mock.call.deleteVolume(osv_matcher),
mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
mock.call.logout()
]
mock_client.assert_has_calls(expected)
def test_migrate_volume_diff_host(self):
conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
'getStorageSystemInfo.return_value': {
'serialNumber': 'different'},
}
mock_client = self.setup_driver(mock_conf=conf)
volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
'id': HP3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'status': 'available',
'host': HP3PARBaseDriver.FAKE_HOST,
'source_volid': HP3PARBaseDriver.VOLUME_ID}
loc_info = 'HP3PARDriver:1234:CPG-FC1'
host = {'host': 'stack@3parfc1',
'capabilities': {'location_info': loc_info}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((False, None), result)
def test_migrate_volume_diff_domain(self):
conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
'getStorageSystemInfo.return_value': {
'serialNumber': '1234'},
'getTask.return_value': {
'status': 1},
'getCPG.side_effect':
lambda x: {'OpenStackCPG': {'domain': 'OpenStack'}}.get(x, {})
}
mock_client = self.setup_driver(mock_conf=conf)
volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
'id': HP3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'status': 'available',
'host': HP3PARBaseDriver.FAKE_HOST,
'source_volid': HP3PARBaseDriver.VOLUME_ID}
loc_info = 'HP3PARDriver:1234:CPG-FC1'
host = {'host': 'stack@3parfc1',
'capabilities': {'location_info': loc_info}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((False, None), result)
def test_migrate_volume_attached(self):
conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
'getStorageSystemInfo.return_value': {
'serialNumber': '1234'},
'getTask.return_value': {
'status': 1}
}
mock_client = self.setup_driver(mock_conf=conf)
volume = {'name': HP3PARBaseDriver.VOLUME_NAME,
'id': HP3PARBaseDriver.CLONE_ID,
'display_name': 'Foo Volume',
'size': 2,
'status': 'in-use',
'host': HP3PARBaseDriver.FAKE_HOST,
'source_volid': HP3PARBaseDriver.VOLUME_ID}
volume_name_3par = self.driver.common._encode_name(volume['id'])
mock_client.getVLUNs.return_value = {
'members': [{'volumeName': 'osv-' + volume_name_3par}]}
loc_info = 'HP3PARDriver:1234:CPG-FC1'
host = {'host': 'stack@3parfc1',
'capabilities': {'location_info': loc_info}}
result = self.driver.migrate_volume(context.get_admin_context(),
volume, host)
self.assertIsNotNone(result)
self.assertEqual((False, None), result)
def test_attach_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.attach_volume(context.get_admin_context(),
self.volume,
'abcdef',
'newhost',
'/dev/vdb')
expected = [
mock.call.setVolumeMetaData(
self.VOLUME_3PAR_NAME,
'HPQ-CS-instance_uuid',
'abcdef')]
mock_client.assert_has_calls(expected)
# test the exception
mock_client.setVolumeMetaData.side_effect = Exception('Custom ex')
self.assertRaises(exception.CinderException,
self.driver.attach_volume,
context.get_admin_context(),
self.volume,
'abcdef',
'newhost',
'/dev/vdb')
def test_detach_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.detach_volume(context.get_admin_context(), self.volume)
expected = [
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME,
'HPQ-CS-instance_uuid')]
mock_client.assert_has_calls(expected)
# test the exception
mock_client.removeVolumeMetaData.side_effect = Exception('Custom ex')
self.assertRaises(exception.CinderException,
self.driver.detach_volume,
context.get_admin_context(),
self.volume)
def test_create_snapshot(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.create_snapshot(self.snapshot)
commet = (
'{"volume_id": "761fc5e5-5191-4ec7-aeba-33e36de44156",'
' "display_name": "fakesnap",'
' "description": "test description name",'
' "volume_name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.createSnapshot(
'oss-L4I73ONuTci9Fd4ceij-MQ',
'osv-dh-F5VGRTseuujPjbeRBVg',
{
'comment': commet,
'readOnly': True}),
mock.call.logout()]
mock_client.assert_has_calls(expected)
def test_delete_snapshot(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.delete_snapshot(self.snapshot)
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.deleteVolume('oss-L4I73ONuTci9Fd4ceij-MQ'),
mock.call.logout()]
mock_client.assert_has_calls(expected)
def test_delete_snapshot_in_use(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.create_snapshot(self.snapshot)
self.driver.create_volume_from_snapshot(self.volume, self.snapshot)
ex = hpexceptions.HTTPConflict("In use")
mock_client.deleteVolume = mock.Mock(side_effect=ex)
# Deleting the snapshot that a volume is dependent on should fail
self.assertRaises(exception.SnapshotIsBusy,
self.driver.delete_snapshot,
self.snapshot)
def test_delete_snapshot_not_found(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.create_snapshot(self.snapshot)
try:
ex = hpexceptions.HTTPNotFound("not found")
mock_client.deleteVolume = mock.Mock(side_effect=ex)
self.driver.delete_snapshot(self.snapshot)
except Exception:
self.fail("Deleting a snapshot that is missing should act as if "
"it worked.")
def test_create_volume_from_snapshot(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
self.driver.create_volume_from_snapshot(self.volume, self.snapshot)
comment = (
'{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
' "display_name": "Foo Volume",'
' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False}),
mock.call.logout()]
mock_client.assert_has_calls(expected)
volume = self.volume.copy()
volume['size'] = 1
self.assertRaises(exception.InvalidInput,
self.driver.create_volume_from_snapshot,
volume, self.snapshot)
def test_create_volume_from_snapshot_and_extend(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
'getTask.return_value': {
'status': 1},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {}
}
mock_client = self.setup_driver(mock_conf=conf)
volume = self.volume.copy()
volume['size'] = self.volume['size'] + 10
self.driver.create_volume_from_snapshot(volume, self.snapshot)
comment = (
'{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
' "display_name": "Foo Volume",'
' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')
volume_name_3par = self.driver.common._encode_name(volume['id'])
osv_matcher = 'osv-' + volume_name_3par
omv_matcher = 'omv-' + volume_name_3par
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ',
{
'comment': comment,
'readOnly': False}),
mock.call.copyVolume(osv_matcher, omv_matcher, mock.ANY, mock.ANY),
mock.call.getTask(mock.ANY),
mock.call.getVolume(osv_matcher),
mock.call.deleteVolume(osv_matcher),
mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
mock.call.growVolume(osv_matcher, 10 * 1024),
mock.call.logout()]
mock_client.assert_has_calls(expected)
def test_create_volume_from_snapshot_and_extend_copy_fail(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
'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)
volume = self.volume.copy()
volume['size'] = self.volume['size'] + 10
self.assertRaises(exception.CinderException,
self.driver.create_volume_from_snapshot,
volume, self.snapshot)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_from_snapshot_qos(self, _mock_volume_types):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
_mock_volume_types.return_value = {
'name': 'gold',
'extra_specs': {
'cpg': HP3PAR_CPG,
'snap_cpg': HP3PAR_CPG_SNAP,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
'volume_type': self.volume_type}}
self.driver.create_volume_from_snapshot(self.volume_qos, self.snapshot)
comment = (
'{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
' "display_name": "Foo Volume",'
' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.createSnapshot(
self.VOLUME_3PAR_NAME,
'oss-L4I73ONuTci9Fd4ceij-MQ', {
'comment': comment,
'readOnly': False}),
mock.call.logout()]
mock_client.assert_has_calls(expected)
volume = self.volume.copy()
volume['size'] = 1
self.assertRaises(exception.InvalidInput,
self.driver.create_volume_from_snapshot,
volume, self.snapshot)
def test_terminate_connection(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getHostVLUNs.return_value = [
{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0}]
self.driver.terminate_connection(
self.volume,
self.connector,
force=True)
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
self.FAKE_HOST),
mock.call.deleteHost(self.FAKE_HOST),
mock.call.logout()]
mock_client.assert_has_calls(expected)
def test_update_volume_key_value_pair(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
key = 'a'
value = 'b'
self.driver.common.update_volume_key_value_pair(
self.volume,
key,
value)
expected = [
mock.call.setVolumeMetaData(self.VOLUME_3PAR_NAME, key, value)]
mock_client.assert_has_calls(expected)
# check exception
mock_client.setVolumeMetaData.side_effect = Exception('fake')
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.common.update_volume_key_value_pair,
self.volume,
None,
'b')
def test_clear_volume_key_value_pair(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
key = 'a'
self.driver.common.clear_volume_key_value_pair(self.volume, key)
expected = [
mock.call.removeVolumeMetaData(self.VOLUME_3PAR_NAME, key)]
mock_client.assert_has_calls(expected)
# check the exception
mock_client.removeVolumeMetaData.side_effect = Exception('fake')
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.common.clear_volume_key_value_pair,
self.volume,
None)
def test_extend_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
grow_size = 3
old_size = self.volume['size']
new_size = old_size + grow_size
self.driver.extend_volume(self.volume, str(new_size))
growth_size_mib = grow_size * units.KiB
expected = [
mock.call.growVolume(self.VOLUME_3PAR_NAME, growth_size_mib)]
mock_client.assert_has_calls(expected)
def test_extend_volume_non_base(self):
extend_ex = hpexceptions.HTTPForbidden(error={'code': 150})
conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
'getTask.return_value': {
'status': 1},
'getCPG.return_value': {},
'copyVolume.return_value': {'taskid': 1},
'getVolume.return_value': {},
# Throw an exception first time only
'growVolume.side_effect': [extend_ex,
None],
}
mock_client = self.setup_driver(mock_conf=conf)
grow_size = 3
old_size = self.volume['size']
new_size = old_size + grow_size
self.driver.extend_volume(self.volume, str(new_size))
self.assertEqual(2, mock_client.growVolume.call_count)
def test_extend_volume_non_base_failure(self):
extend_ex = hpexceptions.HTTPForbidden(error={'code': 150})
conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
'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)
grow_size = 3
old_size = self.volume['size']
new_size = old_size + grow_size
self.assertRaises(hpexceptions.HTTPForbidden,
self.driver.extend_volume,
self.volume,
str(new_size))
def test_get_ports(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getPorts.return_value = {
'members': [
{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.120.252',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8},
{'portWWN': '20210002AC00383D',
'protocol': 1,
'linkState': 4,
'mode': 2,
'device': ['cage2'],
'nodeWWN': '20210002AC00383D',
'type': 2,
'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]}
ports = self.driver.common.get_ports()['members']
self.assertEqual(len(ports), 3)
def test_get_by_qos_spec_with_scoping(self):
self.setup_driver()
qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS)
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:maxIOPS": "100",
"qos:maxBWS": "50",
"qos:minIOPS": "10",
"qos:minBWS": "20",
"qos:latency": "5",
"qos:priority": "high"})
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
qos = self.driver.common._get_qos_by_volume_type(type_ref)
self.assertEqual(qos, {'maxIOPS': '1000', 'maxBWS': '50',
'minIOPS': '100', 'minBWS': '25',
'latency': '25', 'priority': 'low'})
def test_get_by_qos_spec(self):
self.setup_driver()
qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS_SPECS)
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:maxIOPS": "100",
"qos:maxBWS": "50",
"qos:minIOPS": "10",
"qos:minBWS": "20",
"qos:latency": "5",
"qos:priority": "high"})
qos_specs.associate_qos_with_type(self.ctxt,
qos_ref['id'],
type_ref['id'])
type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
qos = self.driver.common._get_qos_by_volume_type(type_ref)
self.assertEqual(qos, {'maxIOPS': '1000', 'maxBWS': '50',
'minIOPS': '100', 'minBWS': '25',
'latency': '25', 'priority': 'low'})
def test_get_by_qos_by_type_only(self):
self.setup_driver()
type_ref = volume_types.create(self.ctxt,
"type1", {"qos:maxIOPS": "100",
"qos:maxBWS": "50",
"qos:minIOPS": "10",
"qos:minBWS": "20",
"qos:latency": "5",
"qos:priority": "high"})
type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id'])
qos = self.driver.common._get_qos_by_volume_type(type_ref)
self.assertEqual(qos, {'maxIOPS': '100', 'maxBWS': '50',
'minIOPS': '10', 'minBWS': '20',
'latency': '5', 'priority': 'high'})
class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
properties = {
'driver_volume_type': 'fibre_channel',
'data': {
'target_lun': 90,
'target_wwn': ['0987654321234', '123456789000987'],
'target_discovered': True,
'initiator_target_map': {'123456789012345':
['0987654321234', '123456789000987'],
'123456789054321':
['0987654321234', '123456789000987'],
}}}
def setUp(self):
super(TestHP3PARFCDriver, self).setUp()
def tearDown(self):
super(TestHP3PARFCDriver, self).tearDown()
def setup_driver(self, config=None, mock_conf=None):
self.ctxt = context.get_admin_context()
mock_client = self.setup_mock_client(
conf=config,
m_conf=mock_conf,
driver=hpfcdriver.HP3PARFCDriver)
expected = [
mock.call.setSSHOptions(
HP3PAR_SAN_IP,
HP3PAR_USER_NAME,
HP3PAR_USER_PASS,
privatekey=HP3PAR_SAN_SSH_PRIVATE,
port=HP3PAR_SAN_SSH_PORT,
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getCPG(HP3PAR_CPG),
mock.call.logout()]
mock_client.assert_has_calls(expected)
mock_client.reset_mock()
return mock_client
def test_initialize_connection(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 1,
'slot': 2},
'vendor': None,
'wwn': self.wwn[0]},
{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 0,
'slot': 2},
'vendor': None,
'wwn': self.wwn[1]}]}]
mock_client.findHost.return_value = self.FAKE_HOST
mock_client.getHostVLUNs.return_value = [
{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': 90, 'type': 0}]
mock_client.getPorts.return_value = {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]}
result = self.driver.initialize_connection(self.volume, self.connector)
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.ANY,
mock.call.getHost(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.logout()]
mock_client.assert_has_calls(expected)
self.assertDictMatch(result, self.properties)
def test_terminate_connection(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getHostVLUNs.return_value = [
{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': None, 'type': 0}]
mock_client.getPorts.return_value = {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]}
self.driver.terminate_connection(
self.volume,
self.connector,
force=True)
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME,
None,
self.FAKE_HOST),
mock.call.deleteHost(self.FAKE_HOST),
mock.call.getPorts(),
mock.call.logout()]
mock_client.assert_has_calls(expected)
def test_get_volume_stats(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getCPG.return_value = self.cpgs[0]
mock_client.getStorageSystemInfo.return_value = {'serialNumber':
'1234'}
stats = self.driver.get_volume_stats(True)
self.assertEqual(stats['storage_protocol'], 'FC')
self.assertEqual(stats['total_capacity_gb'], 'infinite')
self.assertEqual(stats['free_capacity_gb'], 'infinite')
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getStorageSystemInfo(),
mock.call.logout()]
mock_client.assert_has_calls(expected)
stats = self.driver.get_volume_stats(True)
self.assertEqual(stats['storage_protocol'], 'FC')
self.assertEqual(stats['total_capacity_gb'], 'infinite')
self.assertEqual(stats['free_capacity_gb'], 'infinite')
cpg2 = self.cpgs[0].copy()
cpg2.update({'SDGrowth': {'limitMiB': 8192}})
mock_client.getCPG.return_value = cpg2
const = 0.0009765625
stats = self.driver.get_volume_stats(True)
self.assertEqual(stats['storage_protocol'], 'FC')
total_capacity_gb = 8192 * const
self.assertEqual(stats['total_capacity_gb'], total_capacity_gb)
free_capacity_gb = int(
(8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const)
self.assertEqual(stats['free_capacity_gb'], free_capacity_gb)
self.driver.common.client.deleteCPG(HP3PAR_CPG)
self.driver.common.client.createCPG(HP3PAR_CPG, {})
def test_create_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST,
'FCPaths': [{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 1,
'slot': 2},
'vendor': None,
'wwn': self.wwn[0]},
{'driverVersion': None,
'firmwareVersion': None,
'hostSpeed': 0,
'model': None,
'portPos': {'cardPort': 1, 'node': 0,
'slot': 2},
'vendor': None,
'wwn': self.wwn[1]}]}]
mock_client.findHost.return_value = None
mock_client.getVLUN.return_value = {'lun': 186}
host = self.driver._create_host(self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.findHost(wwn='123456789012345'),
mock.call.findHost(wwn='123456789054321'),
mock.call.createHost(
self.FAKE_HOST,
FCWwns=['123456789012345', '123456789054321'],
optional={'domain': None, 'persona': 1}),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(host['name'], self.FAKE_HOST)
def test_create_invalid_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpexceptions.HTTPNotFound('Host not found.'), {
'name': 'fakehost.foo',
'FCPaths': [{'wwn': '123456789012345'}, {
'wwn': '123456789054321'}]}]
mock_client.findHost.return_value = 'fakehost.foo'
host = self.driver._create_host(self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost('fakehost'),
mock.call.findHost(wwn='123456789012345'),
mock.call.getHost('fakehost.foo')]
mock_client.assert_has_calls(expected)
self.assertEqual(host['name'], 'fakehost.foo')
def test_create_modify_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [{
'name': self.FAKE_HOST, 'FCPaths': []},
{'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'}, {
'wwn': '123456789054321'}]}]
host = self.driver._create_host(self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost('fakehost'),
mock.call.modifyHost(
'fakehost', {
'FCWWNs': ['123456789012345', '123456789054321'],
'pathOperation': 1}),
mock.call.getHost('fakehost')]
mock_client.assert_has_calls(expected)
self.assertEqual(host['name'], self.FAKE_HOST)
self.assertEqual(len(host['FCPaths']), 2)
def test_modify_host_with_new_wwn(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
getHost_ret1 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789054321'}]}
getHost_ret2 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}
mock_client.getHost.side_effect = [getHost_ret1, getHost_ret2]
host = self.driver._create_host(self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost('fakehost'),
mock.call.modifyHost(
'fakehost', {
'FCWWNs': ['123456789012345'], 'pathOperation': 1}),
mock.call.getHost('fakehost')]
mock_client.assert_has_calls(expected)
self.assertEqual(host['name'], self.FAKE_HOST)
self.assertEqual(len(host['FCPaths']), 2)
def test_modify_host_with_unknown_wwn_and_new_wwn(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
getHost_ret1 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789054321'},
{'wwn': 'xxxxxxxxxxxxxxx'}]}
getHost_ret2 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'},
{'wwn': 'xxxxxxxxxxxxxxx'}]}
mock_client.getHost.side_effect = [getHost_ret1, getHost_ret2]
host = self.driver._create_host(self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost('fakehost'),
mock.call.modifyHost(
'fakehost', {
'FCWWNs': ['123456789012345'], 'pathOperation': 1}),
mock.call.getHost('fakehost')]
mock_client.assert_has_calls(expected)
self.assertEqual(host['name'], self.FAKE_HOST)
self.assertEqual(len(host['FCPaths']), 3)
class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
TARGET_IQN = 'iqn.2000-05.com.3pardata:21810002ac00383d'
TARGET_LUN = 186
properties = {
'driver_volume_type': 'iscsi',
'data':
{'target_discovered': True,
'target_iqn': TARGET_IQN,
'target_lun': TARGET_LUN,
'target_portal': '1.1.1.2:1234'}}
def setUp(self):
super(TestHP3PARISCSIDriver, self).setUp()
def tearDown(self):
super(TestHP3PARISCSIDriver, self).tearDown()
def setup_driver(self, config=None, mock_conf=None):
self.ctxt = context.get_admin_context()
# setup_mock_client default config, if necessary
if mock_conf is None:
mock_conf = {
'getPorts.return_value': {
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]}}
mock_client = self.setup_mock_client(
conf=config,
m_conf=mock_conf,
driver=hpdriver.HP3PARISCSIDriver)
expected = [
mock.call.setSSHOptions(
HP3PAR_SAN_IP,
HP3PAR_USER_NAME,
HP3PAR_USER_PASS,
privatekey=HP3PAR_SAN_SSH_PRIVATE,
port=HP3PAR_SAN_SSH_PORT,
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getCPG(HP3PAR_CPG),
mock.call.logout(),
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getPorts(),
mock.call.logout()]
mock_client.assert_has_calls(expected)
mock_client.reset_mock()
return mock_client
def test_initialize_connection(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.findHost.return_value = self.FAKE_HOST
mock_client.getHostVLUNs.return_value = [
{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN, 'type': 0}]
result = self.driver.initialize_connection(self.volume, self.connector)
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
mock.call.getHost(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname='fakehost',
portPos={'node': 8, 'slot': 1, 'cardPort': 1}),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.logout()]
mock_client.assert_has_calls(expected)
self.assertDictMatch(result, self.properties)
def test_get_volume_stats(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getCPG.return_value = self.cpgs[0]
mock_client.getStorageSystemInfo.return_value = {'serialNumber':
'1234'}
stats = self.driver.get_volume_stats(True)
self.assertEqual(stats['storage_protocol'], 'iSCSI')
self.assertEqual(stats['total_capacity_gb'], 'infinite')
self.assertEqual(stats['free_capacity_gb'], 'infinite')
expected = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getStorageSystemInfo(),
mock.call.logout()]
mock_client.assert_has_calls(expected)
self.assertEqual(stats['storage_protocol'], 'iSCSI')
self.assertEqual(stats['total_capacity_gb'], 'infinite')
self.assertEqual(stats['free_capacity_gb'], 'infinite')
cpg2 = self.cpgs[0].copy()
cpg2.update({'SDGrowth': {'limitMiB': 8192}})
mock_client.getCPG.return_value = cpg2
const = 0.0009765625
stats = self.driver.get_volume_stats(True)
self.assertEqual(stats['storage_protocol'], 'iSCSI')
total_capacity_gb = 8192 * const
self.assertEqual(stats['total_capacity_gb'], total_capacity_gb)
free_capacity_gb = int(
(8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const)
self.assertEqual(stats['free_capacity_gb'], free_capacity_gb)
def test_create_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.findHost.return_value = None
mock_client.getVLUN.return_value = {'lun': self.TARGET_LUN}
host = self.driver._create_host(self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
mock.call.createHost(
self.FAKE_HOST,
optional={'domain': None, 'persona': 1},
iscsiNames=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(host['name'], self.FAKE_HOST)
def test_create_invalid_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpexceptions.HTTPNotFound('Host not found.'),
{'name': 'fakehost.foo'}]
mock_client.findHost.return_value = 'fakehost.foo'
host = self.driver._create_host(self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
mock.call.getHost('fakehost.foo')]
mock_client.assert_has_calls(expected)
self.assertEqual(host['name'], 'fakehost.foo')
def test_create_modify_host(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
{'name': self.FAKE_HOST, 'FCPaths': []},
{'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}]
host = self.driver._create_host(self.volume, self.connector)
expected = [
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
mock.call.getCPG(HP3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.modifyHost(
self.FAKE_HOST,
{'pathOperation': 1,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.getHost(self.FAKE_HOST)]
mock_client.assert_has_calls(expected)
self.assertEqual(host['name'], self.FAKE_HOST)
self.assertEqual(len(host['FCPaths']), 2)
def test_get_least_used_nsp_for_host_single(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getPorts.return_value = PORTS_RET
mock_client.getVLUNs.return_value = VLUNS1_RET
#Setup a single ISCSI IP
iscsi_ips = ["10.10.220.253"]
self.driver.configuration.hp3par_iscsi_ips = iscsi_ips
self.driver.initialize_iscsi_ports()
nsp = self.driver._get_least_used_nsp_for_host('newhost')
self.assertEqual(nsp, "1:8:1")
def test_get_least_used_nsp_for_host_new(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getPorts.return_value = PORTS_RET
mock_client.getVLUNs.return_value = VLUNS1_RET
#Setup two ISCSI IPs
iscsi_ips = ["10.10.220.252", "10.10.220.253"]
self.driver.configuration.hp3par_iscsi_ips = iscsi_ips
self.driver.initialize_iscsi_ports()
# Host 'newhost' does not yet have any iscsi paths,
# so the 'least used' is returned
nsp = self.driver._get_least_used_nsp_for_host('newhost')
self.assertEqual(nsp, "1:8:2")
def test_get_least_used_nsp_for_host_reuse(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getPorts.return_value = PORTS_RET
mock_client.getVLUNs.return_value = VLUNS1_RET
#Setup two ISCSI IPs
iscsi_ips = ["10.10.220.252", "10.10.220.253"]
self.driver.configuration.hp3par_iscsi_ips = iscsi_ips
self.driver.initialize_iscsi_ports()
# hosts 'foo' and 'bar' already have active iscsi paths
# the same one should be used
nsp = self.driver._get_least_used_nsp_for_host('foo')
self.assertEqual(nsp, "1:8:2")
nsp = self.driver._get_least_used_nsp_for_host('bar')
self.assertEqual(nsp, "1:8:1")
def test_get_least_used_nps_for_host_fc(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getPorts.return_value = PORTS1_RET
mock_client.getVLUNs.return_value = VLUNS5_RET
#Setup two ISCSI IPs
iscsi_ips = ["10.10.220.252", "10.10.220.253"]
self.driver.configuration.hp3par_iscsi_ips = iscsi_ips
self.driver.initialize_iscsi_ports()
nsp = self.driver._get_least_used_nsp_for_host('newhost')
self.assertNotEqual(nsp, "0:6:3")
self.assertEqual(nsp, "1:8:1")
def test_invalid_iscsi_ip(self):
config = self.setup_configuration()
config.hp3par_iscsi_ips = ['10.10.220.250', '10.10.220.251']
config.iscsi_ip_address = '10.10.10.10'
mock_conf = {
'getPorts.return_value': {
'members': [
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.220.252',
'linkState': 4,
'device': [],
'iSCSIName': self.TARGET_IQN,
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': self.TARGET_IQN,
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8}]}}
# no valid ip addr should be configured.
self.assertRaises(exception.InvalidInput,
self.setup_driver,
config=config,
mock_conf=mock_conf)
def test_get_least_used_nsp(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
ports = [
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2}, 'active': True},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 2}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}]
mock_client.getVLUNs.return_value = {'members': ports}
# in use count
vluns = self.driver.common.client.getVLUNs()
nsp = self.driver._get_least_used_nsp(vluns['members'],
['0:2:1', '1:8:1'])
self.assertEqual(nsp, '1:8:1')
ports = [
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}]
mock_client.getVLUNs.return_value = {'members': ports}
# in use count
vluns = self.driver.common.client.getVLUNs()
nsp = self.driver._get_least_used_nsp(vluns['members'],
['0:2:1', '1:2:1'])
self.assertEqual(nsp, '1:2:1')
ports = [
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True},
{'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}]
mock_client.getVLUNs.return_value = {'members': ports}
# in use count
vluns = self.driver.common.client.getVLUNs()
nsp = self.driver._get_least_used_nsp(vluns['members'],
['1:1:1', '1:2:1'])
self.assertEqual(nsp, '1:1:1')
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}}]})