Remove Violin volume drivers
These drivers were marked as deprecated and unsupported in Pike due to lack of CI. This has not changed and they will now be removed. Change-Id: I935990d09f1374a8789b2a0dad8e8a334aa39abc
This commit is contained in:
parent
6d9b747787
commit
46ee7ebdeb
@ -1198,31 +1198,6 @@ class XIODriverException(VolumeDriverException):
|
|||||||
message = _("X-IO Volume Driver exception!")
|
message = _("X-IO Volume Driver exception!")
|
||||||
|
|
||||||
|
|
||||||
# Violin Memory drivers
|
|
||||||
class ViolinInvalidBackendConfig(VolumeDriverException):
|
|
||||||
message = _("Volume backend config is invalid: %(reason)s")
|
|
||||||
|
|
||||||
|
|
||||||
class ViolinRequestRetryTimeout(VolumeDriverException):
|
|
||||||
message = _("Backend service retry timeout hit: %(timeout)s sec")
|
|
||||||
|
|
||||||
|
|
||||||
class ViolinBackendErr(VolumeBackendAPIException):
|
|
||||||
message = _("Backend reports: %(message)s")
|
|
||||||
|
|
||||||
|
|
||||||
class ViolinBackendErrExists(VolumeBackendAPIException):
|
|
||||||
message = _("Backend reports: item already exists")
|
|
||||||
|
|
||||||
|
|
||||||
class ViolinBackendErrNotFound(NotFound):
|
|
||||||
message = _("Backend reports: item not found")
|
|
||||||
|
|
||||||
|
|
||||||
class ViolinResourceNotFound(NotFound):
|
|
||||||
message = _("Backend reports: %(message)s")
|
|
||||||
|
|
||||||
|
|
||||||
class BadHTTPResponseStatus(VolumeDriverException):
|
class BadHTTPResponseStatus(VolumeDriverException):
|
||||||
message = _("Bad HTTP response status %(status)s")
|
message = _("Bad HTTP response status %(status)s")
|
||||||
|
|
||||||
|
@ -169,8 +169,6 @@ from cinder.volume.drivers.synology import synology_common as \
|
|||||||
cinder_volume_drivers_synology_synologycommon
|
cinder_volume_drivers_synology_synologycommon
|
||||||
from cinder.volume.drivers import tegile as cinder_volume_drivers_tegile
|
from cinder.volume.drivers import tegile as cinder_volume_drivers_tegile
|
||||||
from cinder.volume.drivers import tintri as cinder_volume_drivers_tintri
|
from cinder.volume.drivers import tintri as cinder_volume_drivers_tintri
|
||||||
from cinder.volume.drivers.violin import v7000_common as \
|
|
||||||
cinder_volume_drivers_violin_v7000common
|
|
||||||
from cinder.volume.drivers.vmware import vmdk as \
|
from cinder.volume.drivers.vmware import vmdk as \
|
||||||
cinder_volume_drivers_vmware_vmdk
|
cinder_volume_drivers_vmware_vmdk
|
||||||
from cinder.volume.drivers import vzstorage as cinder_volume_drivers_vzstorage
|
from cinder.volume.drivers import vzstorage as cinder_volume_drivers_vzstorage
|
||||||
@ -368,7 +366,6 @@ def list_opts():
|
|||||||
cinder_volume_drivers_synology_synologycommon.cinder_opts,
|
cinder_volume_drivers_synology_synologycommon.cinder_opts,
|
||||||
cinder_volume_drivers_tegile.tegile_opts,
|
cinder_volume_drivers_tegile.tegile_opts,
|
||||||
cinder_volume_drivers_tintri.tintri_opts,
|
cinder_volume_drivers_tintri.tintri_opts,
|
||||||
cinder_volume_drivers_violin_v7000common.violin_opts,
|
|
||||||
cinder_volume_drivers_vmware_vmdk.vmdk_opts,
|
cinder_volume_drivers_vmware_vmdk.vmdk_opts,
|
||||||
cinder_volume_drivers_vzstorage.vzstorage_opts,
|
cinder_volume_drivers_vzstorage.vzstorage_opts,
|
||||||
cinder_volume_drivers_windows_smbfs.volume_opts,
|
cinder_volume_drivers_windows_smbfs.volume_opts,
|
||||||
|
@ -1,366 +0,0 @@
|
|||||||
# Copyright 2016 Violin Memory, Inc.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Tests for Violin Memory 7000 Series All-Flash Array ISCSI Driver
|
|
||||||
"""
|
|
||||||
|
|
||||||
import mock
|
|
||||||
|
|
||||||
from cinder import exception
|
|
||||||
from cinder import test
|
|
||||||
from cinder.tests.unit.volume.drivers.violin import \
|
|
||||||
fake_vmem_client as vmemclient
|
|
||||||
from cinder.volume import configuration as conf
|
|
||||||
from cinder.volume.drivers.violin import v7000_common
|
|
||||||
from cinder.volume.drivers.violin import v7000_iscsi
|
|
||||||
|
|
||||||
VOLUME_ID = "abcdabcd-1234-abcd-1234-abcdeffedcba"
|
|
||||||
VOLUME = {
|
|
||||||
"name": "volume-" + VOLUME_ID,
|
|
||||||
"id": VOLUME_ID,
|
|
||||||
"display_name": "fake_volume",
|
|
||||||
"size": 2,
|
|
||||||
"host": "myhost",
|
|
||||||
"volume_type": None,
|
|
||||||
"volume_type_id": None,
|
|
||||||
}
|
|
||||||
SNAPSHOT_ID = "abcdabcd-1234-abcd-1234-abcdeffedcbb"
|
|
||||||
SNAPSHOT = {
|
|
||||||
"name": "snapshot-" + SNAPSHOT_ID,
|
|
||||||
"id": SNAPSHOT_ID,
|
|
||||||
"volume_id": VOLUME_ID,
|
|
||||||
"volume_name": "volume-" + VOLUME_ID,
|
|
||||||
"volume_size": 2,
|
|
||||||
"display_name": "fake_snapshot",
|
|
||||||
"volume": VOLUME,
|
|
||||||
}
|
|
||||||
SRC_VOL_ID = "abcdabcd-1234-abcd-1234-abcdeffedcbc"
|
|
||||||
SRC_VOL = {
|
|
||||||
"name": "volume-" + SRC_VOL_ID,
|
|
||||||
"id": SRC_VOL_ID,
|
|
||||||
"display_name": "fake_src_vol",
|
|
||||||
"size": 2,
|
|
||||||
"host": "myhost",
|
|
||||||
"volume_type": None,
|
|
||||||
"volume_type_id": None,
|
|
||||||
}
|
|
||||||
SRC_VOL_ID = "abcdabcd-1234-abcd-1234-abcdeffedcbc"
|
|
||||||
SRC_VOL = {
|
|
||||||
"name": "volume-" + SRC_VOL_ID,
|
|
||||||
"id": SRC_VOL_ID,
|
|
||||||
"display_name": "fake_src_vol",
|
|
||||||
"size": 2,
|
|
||||||
"host": "myhost",
|
|
||||||
"volume_type": None,
|
|
||||||
"volume_type_id": None,
|
|
||||||
}
|
|
||||||
INITIATOR_IQN = "iqn.1111-22.org.debian:11:222"
|
|
||||||
CONNECTOR = {
|
|
||||||
"initiator": INITIATOR_IQN,
|
|
||||||
"host": "irrelevant",
|
|
||||||
"ip": "1.2.3.4",
|
|
||||||
}
|
|
||||||
TARGET = "iqn.2004-02.com.vmem:%s" % VOLUME['id']
|
|
||||||
|
|
||||||
GET_VOLUME_STATS_RESPONSE = {
|
|
||||||
'vendor_name': 'Violin Memory, Inc.',
|
|
||||||
'reserved_percentage': 0,
|
|
||||||
'QoS_support': False,
|
|
||||||
'free_capacity_gb': 4094,
|
|
||||||
'total_capacity_gb': 2558,
|
|
||||||
}
|
|
||||||
|
|
||||||
CLIENT_INFO = {
|
|
||||||
'issanip_enabled': False,
|
|
||||||
'sanclient_id': 7,
|
|
||||||
'ISCSIDevices':
|
|
||||||
[{'category': 'Virtual Device',
|
|
||||||
'sizeMB': VOLUME['size'] * 1024,
|
|
||||||
'name': VOLUME['id'],
|
|
||||||
'object_id': 'v0000058',
|
|
||||||
'access': 'ReadWrite',
|
|
||||||
'ISCSITarget':
|
|
||||||
{'name': TARGET,
|
|
||||||
'startingLun': '0',
|
|
||||||
'ipAddr': '192.168.91.1 192.168.92.1 192.168.93.1 192.168.94.1',
|
|
||||||
'object_id': '2c68c1a4-67bb-59b3-93df-58bcdf422a66',
|
|
||||||
'access': 'ReadWrite',
|
|
||||||
'isInfiniBand': 'false',
|
|
||||||
'iscsiurl': ''},
|
|
||||||
'type': 'SAN',
|
|
||||||
'lun': '8',
|
|
||||||
'size': VOLUME['size'] * 1024 * 1024}],
|
|
||||||
'name': 'lab-srv3377',
|
|
||||||
'isiscsi_enabled': True,
|
|
||||||
'clusterName': '',
|
|
||||||
'ipAddress': '',
|
|
||||||
'isclustered': False,
|
|
||||||
'username': '',
|
|
||||||
'isbmr_enabled': False,
|
|
||||||
'useracl': None,
|
|
||||||
'isfibrechannel_enabled': False,
|
|
||||||
'iSCSIPolicy':
|
|
||||||
{'initiators': ['iqn.1993-08.org.debian:01:1ebcd244a059'],
|
|
||||||
'authentication':
|
|
||||||
{'mutualCHAP':
|
|
||||||
{'enabled': False,
|
|
||||||
'user': ''},
|
|
||||||
'enabled': False,
|
|
||||||
'defaultUser': ''},
|
|
||||||
'accessType': 'stationary'},
|
|
||||||
'ISCSITargetList':
|
|
||||||
[{'name': 'iqn.2004-02.com.vmem:lab-fsp-mga.openstack',
|
|
||||||
'startingLun': '0',
|
|
||||||
'ipAddr': '192.168.91.1 192.168.92.1 192.168.93.1 192.168.94.1',
|
|
||||||
'object_id': '716cc60a-576a-55f1-bfe3-af4a21ca5554',
|
|
||||||
'access': 'ReadWrite',
|
|
||||||
'isInfiniBand': 'false',
|
|
||||||
'iscsiurl': ''}],
|
|
||||||
'type': 'Windows',
|
|
||||||
'persistent_reservation': True,
|
|
||||||
'isxboot_enabled': False}
|
|
||||||
|
|
||||||
|
|
||||||
class V7000ISCSIDriverTestCase(test.TestCase):
|
|
||||||
"""Test cases for VMEM ISCSI driver."""
|
|
||||||
def setUp(self):
|
|
||||||
super(V7000ISCSIDriverTestCase, self).setUp()
|
|
||||||
self.conf = self.setup_configuration()
|
|
||||||
self.driver = v7000_iscsi.V7000ISCSIDriver(configuration=self.conf)
|
|
||||||
self.driver.gateway_iscsi_ip_addresses = [
|
|
||||||
'192.168.91.1', '192.168.92.1', '192.168.93.1', '192.168.94.1']
|
|
||||||
self.stats = {}
|
|
||||||
self.driver.set_initialized()
|
|
||||||
|
|
||||||
def setup_configuration(self):
|
|
||||||
config = mock.Mock(spec=conf.Configuration)
|
|
||||||
config.volume_backend_name = 'v7000_iscsi'
|
|
||||||
config.san_ip = '8.8.8.8'
|
|
||||||
config.san_login = 'admin'
|
|
||||||
config.san_password = ''
|
|
||||||
config.san_thin_provision = False
|
|
||||||
config.san_is_local = False
|
|
||||||
config.use_igroups = False
|
|
||||||
config.request_timeout = 300
|
|
||||||
return config
|
|
||||||
|
|
||||||
def setup_mock_concerto(self, m_conf=None):
|
|
||||||
"""Create a fake Concerto communication object."""
|
|
||||||
_m_concerto = mock.Mock(name='Concerto',
|
|
||||||
version='1.1.1',
|
|
||||||
spec=vmemclient.mock_client_conf)
|
|
||||||
|
|
||||||
if m_conf:
|
|
||||||
_m_concerto.configure_mock(**m_conf)
|
|
||||||
|
|
||||||
return _m_concerto
|
|
||||||
|
|
||||||
@mock.patch.object(v7000_common.V7000Common, 'check_for_setup_error')
|
|
||||||
def test_check_for_setup_error(self, m_setup_func):
|
|
||||||
"""No setup errors are found."""
|
|
||||||
result = self.driver.check_for_setup_error()
|
|
||||||
m_setup_func.assert_called_with()
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_create_volume(self):
|
|
||||||
"""Volume created successfully."""
|
|
||||||
self.driver.common._create_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.create_volume(VOLUME)
|
|
||||||
|
|
||||||
self.driver.common._create_lun.assert_called_with(VOLUME)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_create_volume_from_snapshot(self):
|
|
||||||
self.driver.common._create_volume_from_snapshot = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.create_volume_from_snapshot(VOLUME, SNAPSHOT)
|
|
||||||
|
|
||||||
self.driver.common._create_volume_from_snapshot.assert_called_with(
|
|
||||||
SNAPSHOT, VOLUME)
|
|
||||||
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_create_cloned_volume(self):
|
|
||||||
self.driver.common._create_lun_from_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.create_cloned_volume(VOLUME, SRC_VOL)
|
|
||||||
|
|
||||||
self.driver.common._create_lun_from_lun.assert_called_with(
|
|
||||||
SRC_VOL, VOLUME)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_delete_volume(self):
|
|
||||||
"""Volume deleted successfully."""
|
|
||||||
self.driver.common._delete_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.delete_volume(VOLUME)
|
|
||||||
|
|
||||||
self.driver.common._delete_lun.assert_called_with(VOLUME)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_extend_volume(self):
|
|
||||||
"""Volume extended successfully."""
|
|
||||||
new_size = 10
|
|
||||||
self.driver.common._extend_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.extend_volume(VOLUME, new_size)
|
|
||||||
|
|
||||||
self.driver.common._extend_lun.assert_called_with(VOLUME, new_size)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_create_snapshot(self):
|
|
||||||
self.driver.common._create_lun_snapshot = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.create_snapshot(SNAPSHOT)
|
|
||||||
self.driver.common._create_lun_snapshot.assert_called_with(SNAPSHOT)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_delete_snapshot(self):
|
|
||||||
self.driver.common._delete_lun_snapshot = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.delete_snapshot(SNAPSHOT)
|
|
||||||
self.driver.common._delete_lun_snapshot.assert_called_with(SNAPSHOT)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_get_volume_stats(self):
|
|
||||||
self.driver._update_volume_stats = mock.Mock()
|
|
||||||
self.driver._update_volume_stats()
|
|
||||||
|
|
||||||
result = self.driver.get_volume_stats(True)
|
|
||||||
|
|
||||||
self.driver._update_volume_stats.assert_called_with()
|
|
||||||
self.assertEqual(self.driver.stats, result)
|
|
||||||
|
|
||||||
def test_update_volume_stats(self):
|
|
||||||
"""Mock query to the backend collects stats on all physical devices."""
|
|
||||||
backend_name = self.conf.volume_backend_name
|
|
||||||
|
|
||||||
self.driver.common._get_volume_stats = mock.Mock(
|
|
||||||
return_value=GET_VOLUME_STATS_RESPONSE,
|
|
||||||
)
|
|
||||||
|
|
||||||
result = self.driver._update_volume_stats()
|
|
||||||
|
|
||||||
self.driver.common._get_volume_stats.assert_called_with(
|
|
||||||
self.conf.san_ip)
|
|
||||||
self.assertEqual(backend_name,
|
|
||||||
self.driver.stats['volume_backend_name'])
|
|
||||||
self.assertEqual('iSCSI',
|
|
||||||
self.driver.stats['storage_protocol'])
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_initialize_connection(self):
|
|
||||||
lun_id = 1
|
|
||||||
response = {'success': True, 'msg': 'None'}
|
|
||||||
|
|
||||||
conf = {
|
|
||||||
'client.create_client.return_value': response,
|
|
||||||
'client.create_iscsi_target.return_value': response,
|
|
||||||
}
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
self.driver._get_iqn = mock.Mock(return_value=TARGET)
|
|
||||||
self.driver._export_lun = mock.Mock(return_value=lun_id)
|
|
||||||
|
|
||||||
props = self.driver.initialize_connection(VOLUME, CONNECTOR)
|
|
||||||
|
|
||||||
self.driver._export_lun.assert_called_with(VOLUME, TARGET, CONNECTOR)
|
|
||||||
self.assertEqual("iscsi", props['driver_volume_type'])
|
|
||||||
self.assertFalse(props['data']['target_discovered'])
|
|
||||||
self.assertEqual(TARGET, props['data']['target_iqn'])
|
|
||||||
self.assertEqual(lun_id, props['data']['target_lun'])
|
|
||||||
self.assertEqual(VOLUME['id'], props['data']['volume_id'])
|
|
||||||
|
|
||||||
def test_terminate_connection(self):
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
self.driver._get_iqn = mock.Mock(return_value=TARGET)
|
|
||||||
self.driver._unexport_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.terminate_connection(VOLUME, CONNECTOR)
|
|
||||||
|
|
||||||
self.driver._unexport_lun.assert_called_with(VOLUME, TARGET, CONNECTOR)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_export_lun(self):
|
|
||||||
lun_id = '1'
|
|
||||||
response = {'success': True, 'msg': 'Assign device successfully'}
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
|
|
||||||
self.driver.common._send_cmd_and_verify = mock.Mock(
|
|
||||||
return_value=response)
|
|
||||||
self.driver._get_lun_id = mock.Mock(return_value=lun_id)
|
|
||||||
|
|
||||||
result = self.driver._export_lun(VOLUME, TARGET, CONNECTOR)
|
|
||||||
|
|
||||||
self.driver.common._send_cmd_and_verify.assert_called_with(
|
|
||||||
self.driver.common.vmem_mg.lun.assign_lun_to_iscsi_target,
|
|
||||||
self.driver._is_lun_id_ready,
|
|
||||||
'Assign device successfully',
|
|
||||||
[VOLUME['id'], TARGET],
|
|
||||||
[VOLUME['id'], CONNECTOR['host']])
|
|
||||||
self.driver._get_lun_id.assert_called_with(
|
|
||||||
VOLUME['id'], CONNECTOR['host'])
|
|
||||||
self.assertEqual(lun_id, result)
|
|
||||||
|
|
||||||
def test_export_lun_fails_with_exception(self):
|
|
||||||
lun_id = '1'
|
|
||||||
response = {'success': False, 'msg': 'Generic error'}
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
self.driver.common._send_cmd_and_verify = mock.Mock(
|
|
||||||
side_effect=exception.ViolinBackendErr(response['msg']))
|
|
||||||
self.driver._get_lun_id = mock.Mock(return_value=lun_id)
|
|
||||||
|
|
||||||
self.assertRaises(exception.ViolinBackendErr,
|
|
||||||
self.driver._export_lun,
|
|
||||||
VOLUME, TARGET, CONNECTOR)
|
|
||||||
|
|
||||||
def test_unexport_lun(self):
|
|
||||||
response = {'success': True, 'msg': 'Unassign device successfully'}
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
self.driver.common._send_cmd = mock.Mock(
|
|
||||||
return_value=response)
|
|
||||||
|
|
||||||
result = self.driver._unexport_lun(VOLUME, TARGET, CONNECTOR)
|
|
||||||
|
|
||||||
self.driver.common._send_cmd.assert_called_with(
|
|
||||||
self.driver.common.vmem_mg.lun.unassign_lun_from_iscsi_target,
|
|
||||||
"Unassign device successfully",
|
|
||||||
VOLUME['id'], TARGET, True)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_is_lun_id_ready(self):
|
|
||||||
lun_id = '1'
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
|
|
||||||
self.driver._get_lun_id = mock.Mock(return_value=lun_id)
|
|
||||||
|
|
||||||
result = self.driver._is_lun_id_ready(
|
|
||||||
VOLUME['id'], CONNECTOR['host'])
|
|
||||||
self.assertTrue(result)
|
|
||||||
|
|
||||||
def test_get_lun_id(self):
|
|
||||||
|
|
||||||
conf = {
|
|
||||||
'client.get_client_info.return_value': CLIENT_INFO,
|
|
||||||
}
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
|
|
||||||
result = self.driver._get_lun_id(VOLUME['id'], CONNECTOR['host'])
|
|
||||||
|
|
||||||
self.assertEqual(8, result)
|
|
@ -1,67 +0,0 @@
|
|||||||
# Copyright 2014 Violin Memory, Inc.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Fake VMEM REST client for testing drivers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import mock
|
|
||||||
|
|
||||||
|
|
||||||
# The following gymnastics to fake an exception class globally is done because
|
|
||||||
# we want to globally model and make available certain exceptions. If we do
|
|
||||||
# not do this, then the real-driver's import will not see our fakes.
|
|
||||||
class NoMatchingObjectIdError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
error = mock.Mock()
|
|
||||||
error.NoMatchingObjectIdError = NoMatchingObjectIdError
|
|
||||||
|
|
||||||
core = mock.Mock()
|
|
||||||
core.attach_mock(error, 'error')
|
|
||||||
|
|
||||||
vmemclient = mock.Mock()
|
|
||||||
vmemclient.__version__ = "unknown"
|
|
||||||
vmemclient.attach_mock(core, 'core')
|
|
||||||
|
|
||||||
sys.modules['vmemclient'] = vmemclient
|
|
||||||
|
|
||||||
mock_client_conf = [
|
|
||||||
'basic',
|
|
||||||
'basic.login',
|
|
||||||
'basic.get_node_values',
|
|
||||||
'basic.save_config',
|
|
||||||
'lun',
|
|
||||||
'lun.export_lun',
|
|
||||||
'lun.unexport_lun',
|
|
||||||
'snapshot',
|
|
||||||
'snapshot.export_lun_snapshot',
|
|
||||||
'snapshot.unexport_lun_snapshot',
|
|
||||||
'iscsi',
|
|
||||||
'iscsi.bind_ip_to_target',
|
|
||||||
'iscsi.create_iscsi_target',
|
|
||||||
'iscsi.delete_iscsi_target',
|
|
||||||
'igroup',
|
|
||||||
'client',
|
|
||||||
'client.get_client_info',
|
|
||||||
'client.create_client',
|
|
||||||
'client.delete_client',
|
|
||||||
'adapter',
|
|
||||||
'adapter.get_fc_info',
|
|
||||||
'pool',
|
|
||||||
'utility',
|
|
||||||
]
|
|
File diff suppressed because it is too large
Load Diff
@ -1,574 +0,0 @@
|
|||||||
# Copyright 2015 Violin Memory, Inc.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Tests for Violin Memory 7000 Series All-Flash Array Fibrechannel Driver
|
|
||||||
"""
|
|
||||||
|
|
||||||
import mock
|
|
||||||
|
|
||||||
from cinder import exception
|
|
||||||
from cinder import test
|
|
||||||
from cinder.tests.unit.volume.drivers.violin \
|
|
||||||
import fake_vmem_client as vmemclient
|
|
||||||
from cinder.volume import configuration as conf
|
|
||||||
from cinder.volume.drivers.violin import v7000_common
|
|
||||||
from cinder.volume.drivers.violin import v7000_fcp
|
|
||||||
|
|
||||||
VOLUME_ID = "abcdabcd-1234-abcd-1234-abcdeffedcba"
|
|
||||||
VOLUME = {
|
|
||||||
"name": "volume-" + VOLUME_ID,
|
|
||||||
"id": VOLUME_ID,
|
|
||||||
"display_name": "fake_volume",
|
|
||||||
"size": 2,
|
|
||||||
"host": "myhost",
|
|
||||||
"volume_type": None,
|
|
||||||
"volume_type_id": None,
|
|
||||||
}
|
|
||||||
SNAPSHOT_ID = "abcdabcd-1234-abcd-1234-abcdeffedcbb"
|
|
||||||
SNAPSHOT = {
|
|
||||||
"name": "snapshot-" + SNAPSHOT_ID,
|
|
||||||
"id": SNAPSHOT_ID,
|
|
||||||
"volume_id": VOLUME_ID,
|
|
||||||
"volume_name": "volume-" + VOLUME_ID,
|
|
||||||
"volume_size": 2,
|
|
||||||
"display_name": "fake_snapshot",
|
|
||||||
"volume": VOLUME,
|
|
||||||
}
|
|
||||||
SRC_VOL_ID = "abcdabcd-1234-abcd-1234-abcdeffedcbc"
|
|
||||||
SRC_VOL = {
|
|
||||||
"name": "volume-" + SRC_VOL_ID,
|
|
||||||
"id": SRC_VOL_ID,
|
|
||||||
"display_name": "fake_src_vol",
|
|
||||||
"size": 2,
|
|
||||||
"host": "myhost",
|
|
||||||
"volume_type": None,
|
|
||||||
"volume_type_id": None,
|
|
||||||
}
|
|
||||||
INITIATOR_IQN = "iqn.1111-22.org.debian:11:222"
|
|
||||||
CONNECTOR = {
|
|
||||||
"initiator": INITIATOR_IQN,
|
|
||||||
"host": "irrelevant",
|
|
||||||
'wwpns': ['50014380186b3f65', '50014380186b3f67'],
|
|
||||||
}
|
|
||||||
FC_TARGET_WWPNS = [
|
|
||||||
'31000024ff45fb22', '21000024ff45fb23',
|
|
||||||
'51000024ff45f1be', '41000024ff45f1bf'
|
|
||||||
]
|
|
||||||
FC_INITIATOR_WWPNS = [
|
|
||||||
'50014380186b3f65', '50014380186b3f67'
|
|
||||||
]
|
|
||||||
FC_FABRIC_MAP = {
|
|
||||||
'fabricA':
|
|
||||||
{'target_port_wwn_list': [FC_TARGET_WWPNS[0], FC_TARGET_WWPNS[1]],
|
|
||||||
'initiator_port_wwn_list': [FC_INITIATOR_WWPNS[0]]},
|
|
||||||
'fabricB':
|
|
||||||
{'target_port_wwn_list': [FC_TARGET_WWPNS[2], FC_TARGET_WWPNS[3]],
|
|
||||||
'initiator_port_wwn_list': [FC_INITIATOR_WWPNS[1]]}
|
|
||||||
}
|
|
||||||
FC_INITIATOR_TARGET_MAP = {
|
|
||||||
FC_INITIATOR_WWPNS[0]: [FC_TARGET_WWPNS[0], FC_TARGET_WWPNS[1]],
|
|
||||||
FC_INITIATOR_WWPNS[1]: [FC_TARGET_WWPNS[2], FC_TARGET_WWPNS[3]]
|
|
||||||
}
|
|
||||||
|
|
||||||
PHY_DEVICES_RESPONSE = {
|
|
||||||
'data':
|
|
||||||
{'physical_devices':
|
|
||||||
[{'availsize': 1099504287744,
|
|
||||||
'availsize_mb': 524284,
|
|
||||||
'category': 'Virtual Device',
|
|
||||||
'connection_type': 'block',
|
|
||||||
'firmware': 'v1.0',
|
|
||||||
'guid': '3cc4d6dd-166d-77d2-4967-00005463f597',
|
|
||||||
'inquiry_string': '000002122b000032BKSC OTHDISK-MFCN01 v1.0',
|
|
||||||
'is_foreign': True,
|
|
||||||
'name': 'BKSC:OTHDISK-MFCN01.000',
|
|
||||||
'object_id': '84b834fb-1f4d-5d3b-b7ae-5796f9868151',
|
|
||||||
'owner': 'example.com',
|
|
||||||
'pool': None,
|
|
||||||
'product': 'OTHDISK-MFCN01',
|
|
||||||
'scsi_address':
|
|
||||||
{'adapter': '98',
|
|
||||||
'channel': '0',
|
|
||||||
'id': '0',
|
|
||||||
'lun': '0',
|
|
||||||
'object_id': '6e0106fc-9c1c-52a2-95c9-396b7a653ac1'},
|
|
||||||
'size': 1099504287744,
|
|
||||||
'size_mb': 1048569,
|
|
||||||
'type': 'Direct-Access',
|
|
||||||
'usedsize': 0,
|
|
||||||
'usedsize_mb': 0,
|
|
||||||
'vendor': 'BKSC',
|
|
||||||
'wwid': 'BKSC OTHDISK-MFCN01 v1.0-0-0-00'},
|
|
||||||
{'availsize': 1099504287744,
|
|
||||||
'availsize_mb': 524284,
|
|
||||||
'category': 'Virtual Device',
|
|
||||||
'connection_type': 'block',
|
|
||||||
'firmware': 'v1.0',
|
|
||||||
'guid': '283b2694-192b-4745-6768-00005463f673',
|
|
||||||
'inquiry_string': '000002122b000032BKSC OTHDISK-MFCN08 v1.0',
|
|
||||||
'is_foreign': False,
|
|
||||||
'name': 'BKSC:OTHDISK-MFCN08.000',
|
|
||||||
'object_id': '8555b888-bf43-5083-a433-f0c7b0282370',
|
|
||||||
'owner': 'example.com',
|
|
||||||
'pool':
|
|
||||||
{'name': 'mga-pool',
|
|
||||||
'object_id': '0818d3de-4437-535f-9cac-cc100a2c9313'},
|
|
||||||
'product': 'OTHDISK-MFCN08',
|
|
||||||
'scsi_address':
|
|
||||||
{'adapter': '98',
|
|
||||||
'channel': '0',
|
|
||||||
'id': '11',
|
|
||||||
'lun': '0',
|
|
||||||
'object_id': '6e0106fc-9c1c-52a2-95c9-396b7a653ac1'},
|
|
||||||
'size': 1099504287744,
|
|
||||||
'size_mb': 1048569,
|
|
||||||
'type': 'Direct-Access',
|
|
||||||
'usedsize': 0,
|
|
||||||
'usedsize_mb': 0,
|
|
||||||
'vendor': 'BKSC',
|
|
||||||
'wwid': 'BKSC OTHDISK-MFCN08 v1.0-0-0-00'},
|
|
||||||
{'availsize': 1099504287744,
|
|
||||||
'availsize_mb': 1048569,
|
|
||||||
'category': 'Virtual Device',
|
|
||||||
'connection_type': 'block',
|
|
||||||
'firmware': 'v1.0',
|
|
||||||
'guid': '7f47db19-019c-707d-0df1-00005463f949',
|
|
||||||
'inquiry_string': '000002122b000032BKSC OTHDISK-MFCN09 v1.0',
|
|
||||||
'is_foreign': False,
|
|
||||||
'name': 'BKSC:OTHDISK-MFCN09.000',
|
|
||||||
'object_id': '62a98898-f8b8-5837-af2b-764f5a72e291',
|
|
||||||
'owner': 'a.b.c.d',
|
|
||||||
'pool':
|
|
||||||
{'name': 'mga-pool',
|
|
||||||
'object_id': '0818d3de-4437-535f-9cac-cc100a2c9313'},
|
|
||||||
'product': 'OTHDISK-MFCN09',
|
|
||||||
'scsi_address':
|
|
||||||
{'adapter': '98',
|
|
||||||
'channel': '0',
|
|
||||||
'id': '12',
|
|
||||||
'lun': '0',
|
|
||||||
'object_id': '6e0106fc-9c1c-52a2-95c9-396b7a653ac1'},
|
|
||||||
'size': 1099504287744,
|
|
||||||
'size_mb': 524284,
|
|
||||||
'type': 'Direct-Access',
|
|
||||||
'usedsize': 0,
|
|
||||||
'usedsize_mb': 0,
|
|
||||||
'vendor': 'BKSC',
|
|
||||||
'wwid': 'BKSC OTHDISK-MFCN09 v1.0-0-0-00'}],
|
|
||||||
'total_physical_devices': 3},
|
|
||||||
'msg': 'Successful',
|
|
||||||
'success': True
|
|
||||||
}
|
|
||||||
|
|
||||||
# The FC_INFO dict returned by the backend is keyed on
|
|
||||||
# object_id of the FC adapter and the values are the
|
|
||||||
# wwmns
|
|
||||||
FC_INFO = {
|
|
||||||
'1a3cdb6a-383d-5ba6-a50b-4ba598074510': ['2100001b9745e25e'],
|
|
||||||
'4a6bc10a-5547-5cc0-94f2-76222a8f8dff': ['2100001b9745e230'],
|
|
||||||
'b21bfff5-d89e-51ff-9920-d990a061d722': ['2100001b9745e25f'],
|
|
||||||
'b508cc6b-f78a-51f9-81cf-47c1aaf53dd1': ['2100001b9745e231']
|
|
||||||
}
|
|
||||||
|
|
||||||
CLIENT_INFO = {
|
|
||||||
'FCPolicy':
|
|
||||||
{'AS400enabled': False,
|
|
||||||
'VSAenabled': False,
|
|
||||||
'initiatorWWPNList': ['50-01-43-80-18-6b-3f-66',
|
|
||||||
'50-01-43-80-18-6b-3f-64']},
|
|
||||||
'FibreChannelDevices':
|
|
||||||
[{'access': 'ReadWrite',
|
|
||||||
'id': 'v0000004',
|
|
||||||
'initiatorWWPN': '*',
|
|
||||||
'lun': '8',
|
|
||||||
'name': 'abcdabcd-1234-abcd-1234-abcdeffedcba',
|
|
||||||
'sizeMB': 10240,
|
|
||||||
'targetWWPN': '*',
|
|
||||||
'type': 'SAN'}]
|
|
||||||
}
|
|
||||||
|
|
||||||
CLIENT_INFO1 = {
|
|
||||||
'FCPolicy':
|
|
||||||
{'AS400enabled': False,
|
|
||||||
'VSAenabled': False,
|
|
||||||
'initiatorWWPNList': ['50-01-43-80-18-6b-3f-66',
|
|
||||||
'50-01-43-80-18-6b-3f-64']},
|
|
||||||
'FibreChannelDevices': []
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class V7000FCPDriverTestCase(test.TestCase):
|
|
||||||
"""Test cases for VMEM FCP driver."""
|
|
||||||
def setUp(self):
|
|
||||||
super(V7000FCPDriverTestCase, self).setUp()
|
|
||||||
self.conf = self.setup_configuration()
|
|
||||||
self.driver = v7000_fcp.V7000FCPDriver(configuration=self.conf)
|
|
||||||
self.driver.common.container = 'myContainer'
|
|
||||||
self.driver.device_id = 'ata-VIOLIN_MEMORY_ARRAY_23109R00000022'
|
|
||||||
self.driver.gateway_fc_wwns = FC_TARGET_WWPNS
|
|
||||||
self.stats = {}
|
|
||||||
self.driver.set_initialized()
|
|
||||||
|
|
||||||
def setup_configuration(self):
|
|
||||||
config = mock.Mock(spec=conf.Configuration)
|
|
||||||
config.volume_backend_name = 'v7000_fcp'
|
|
||||||
config.san_ip = '8.8.8.8'
|
|
||||||
config.san_login = 'admin'
|
|
||||||
config.san_password = ''
|
|
||||||
config.san_thin_provision = False
|
|
||||||
config.san_is_local = False
|
|
||||||
config.request_timeout = 300
|
|
||||||
config.container = 'myContainer'
|
|
||||||
return config
|
|
||||||
|
|
||||||
def setup_mock_concerto(self, m_conf=None):
|
|
||||||
"""Create a fake Concerto communication object."""
|
|
||||||
_m_concerto = mock.Mock(name='Concerto',
|
|
||||||
version='1.1.1',
|
|
||||||
spec=vmemclient.mock_client_conf)
|
|
||||||
|
|
||||||
if m_conf:
|
|
||||||
_m_concerto.configure_mock(**m_conf)
|
|
||||||
|
|
||||||
return _m_concerto
|
|
||||||
|
|
||||||
@mock.patch.object(v7000_common.V7000Common, 'check_for_setup_error')
|
|
||||||
def test_check_for_setup_error(self, m_setup_func):
|
|
||||||
"""No setup errors are found."""
|
|
||||||
result = self.driver.check_for_setup_error()
|
|
||||||
m_setup_func.assert_called_with()
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
@mock.patch.object(v7000_common.V7000Common, 'check_for_setup_error')
|
|
||||||
def test_check_for_setup_error_no_wwn_config(self, m_setup_func):
|
|
||||||
"""No wwns were found during setup."""
|
|
||||||
self.driver.gateway_fc_wwns = []
|
|
||||||
failure = exception.ViolinInvalidBackendConfig
|
|
||||||
self.assertRaises(failure, self.driver.check_for_setup_error)
|
|
||||||
|
|
||||||
def test_create_volume(self):
|
|
||||||
"""Volume created successfully."""
|
|
||||||
self.driver.common._create_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.create_volume(VOLUME)
|
|
||||||
|
|
||||||
self.driver.common._create_lun.assert_called_with(VOLUME)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_create_volume_from_snapshot(self):
|
|
||||||
self.driver.common._create_volume_from_snapshot = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.create_volume_from_snapshot(VOLUME, SNAPSHOT)
|
|
||||||
|
|
||||||
self.driver.common._create_volume_from_snapshot.assert_called_with(
|
|
||||||
SNAPSHOT, VOLUME)
|
|
||||||
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_create_cloned_volume(self):
|
|
||||||
self.driver.common._create_lun_from_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.create_cloned_volume(VOLUME, SRC_VOL)
|
|
||||||
|
|
||||||
self.driver.common._create_lun_from_lun.assert_called_with(
|
|
||||||
SRC_VOL, VOLUME)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_delete_volume(self):
|
|
||||||
"""Volume deleted successfully."""
|
|
||||||
self.driver.common._delete_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.delete_volume(VOLUME)
|
|
||||||
|
|
||||||
self.driver.common._delete_lun.assert_called_with(VOLUME)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_extend_volume(self):
|
|
||||||
"""Volume extended successfully."""
|
|
||||||
new_size = 10
|
|
||||||
self.driver.common._extend_lun = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.extend_volume(VOLUME, new_size)
|
|
||||||
|
|
||||||
self.driver.common._extend_lun.assert_called_with(VOLUME, new_size)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_create_snapshot(self):
|
|
||||||
self.driver.common._create_lun_snapshot = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.create_snapshot(SNAPSHOT)
|
|
||||||
self.driver.common._create_lun_snapshot.assert_called_with(SNAPSHOT)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_delete_snapshot(self):
|
|
||||||
self.driver.common._delete_lun_snapshot = mock.Mock()
|
|
||||||
|
|
||||||
result = self.driver.delete_snapshot(SNAPSHOT)
|
|
||||||
self.driver.common._delete_lun_snapshot.assert_called_with(SNAPSHOT)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_get_volume_stats(self):
|
|
||||||
self.driver._update_volume_stats = mock.Mock()
|
|
||||||
self.driver._update_volume_stats()
|
|
||||||
|
|
||||||
result = self.driver.get_volume_stats(True)
|
|
||||||
|
|
||||||
self.driver._update_volume_stats.assert_called_with()
|
|
||||||
self.assertEqual(self.driver.stats, result)
|
|
||||||
|
|
||||||
@mock.patch('socket.gethostbyaddr')
|
|
||||||
def test_update_volume_stats(self, mock_gethost):
|
|
||||||
"""Test Update Volume Stats.
|
|
||||||
|
|
||||||
Makes a mock query to the backend to collect stats on all physical
|
|
||||||
devices.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def gethostbyaddr(addr):
|
|
||||||
if addr == '8.8.8.8' or addr == 'example.com':
|
|
||||||
return ('example.com', [], ['8.8.8.8'])
|
|
||||||
else:
|
|
||||||
return ('a.b.c.d', [], addr)
|
|
||||||
mock_gethost.side_effect = gethostbyaddr
|
|
||||||
|
|
||||||
backend_name = self.conf.volume_backend_name
|
|
||||||
vendor_name = "Violin Memory, Inc."
|
|
||||||
tot_gb = 2046
|
|
||||||
free_gb = 1022
|
|
||||||
|
|
||||||
phy_devices = "/batch/physicalresource/physicaldevice"
|
|
||||||
|
|
||||||
conf = {
|
|
||||||
'basic.get.side_effect': [PHY_DEVICES_RESPONSE, ],
|
|
||||||
}
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
|
|
||||||
result = self.driver._update_volume_stats()
|
|
||||||
|
|
||||||
calls = [mock.call(phy_devices)]
|
|
||||||
self.driver.common.vmem_mg.basic.get.assert_has_calls(calls)
|
|
||||||
self.assertEqual(tot_gb, self.driver.stats['total_capacity_gb'])
|
|
||||||
self.assertEqual(free_gb, self.driver.stats['free_capacity_gb'])
|
|
||||||
self.assertEqual(backend_name,
|
|
||||||
self.driver.stats['volume_backend_name'])
|
|
||||||
self.assertEqual(vendor_name, self.driver.stats['vendor_name'])
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_get_active_fc_targets(self):
|
|
||||||
"""Test Get Active FC Targets.
|
|
||||||
|
|
||||||
Makes a mock query to the backend to collect all the physical
|
|
||||||
adapters and extract the WWNs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
conf = {
|
|
||||||
'adapter.get_fc_info.return_value': FC_INFO,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
|
|
||||||
result = self.driver._get_active_fc_targets()
|
|
||||||
|
|
||||||
self.assertEqual({'2100001b9745e230', '2100001b9745e25f',
|
|
||||||
'2100001b9745e231', '2100001b9745e25e'},
|
|
||||||
set(result))
|
|
||||||
|
|
||||||
def test_initialize_connection(self):
|
|
||||||
lun_id = 1
|
|
||||||
target_wwns = self.driver.gateway_fc_wwns
|
|
||||||
init_targ_map = {}
|
|
||||||
|
|
||||||
conf = {
|
|
||||||
'client.create_client.return_value': None,
|
|
||||||
}
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
self.driver._export_lun = mock.Mock(return_value=lun_id)
|
|
||||||
self.driver._build_initiator_target_map = mock.Mock(
|
|
||||||
return_value=(target_wwns, init_targ_map))
|
|
||||||
|
|
||||||
props = self.driver.initialize_connection(VOLUME, CONNECTOR)
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg.client.create_client.assert_called_with(
|
|
||||||
name=CONNECTOR['host'], proto='FC', fc_wwns=CONNECTOR['wwpns'])
|
|
||||||
self.driver._export_lun.assert_called_with(VOLUME, CONNECTOR)
|
|
||||||
self.driver._build_initiator_target_map.assert_called_with(
|
|
||||||
CONNECTOR)
|
|
||||||
self.assertEqual("fibre_channel", props['driver_volume_type'])
|
|
||||||
self.assertTrue(props['data']['target_discovered'])
|
|
||||||
self.assertEqual(self.driver.gateway_fc_wwns,
|
|
||||||
props['data']['target_wwn'])
|
|
||||||
self.assertEqual(lun_id, props['data']['target_lun'])
|
|
||||||
|
|
||||||
def test_terminate_connection(self):
|
|
||||||
target_wwns = self.driver.gateway_fc_wwns
|
|
||||||
init_targ_map = {}
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
self.driver._unexport_lun = mock.Mock()
|
|
||||||
self.driver._is_initiator_connected_to_array = mock.Mock(
|
|
||||||
return_value=False)
|
|
||||||
self.driver._build_initiator_target_map = mock.Mock(
|
|
||||||
return_value=(target_wwns, init_targ_map))
|
|
||||||
|
|
||||||
props = self.driver.terminate_connection(VOLUME, CONNECTOR)
|
|
||||||
|
|
||||||
self.driver._unexport_lun.assert_called_with(VOLUME, CONNECTOR)
|
|
||||||
self.driver._is_initiator_connected_to_array.assert_called_with(
|
|
||||||
CONNECTOR)
|
|
||||||
self.driver._build_initiator_target_map.assert_called_with(
|
|
||||||
CONNECTOR)
|
|
||||||
self.assertEqual("fibre_channel", props['driver_volume_type'])
|
|
||||||
self.assertEqual(target_wwns, props['data']['target_wwn'])
|
|
||||||
self.assertEqual(init_targ_map, props['data']['initiator_target_map'])
|
|
||||||
|
|
||||||
def test_export_lun(self):
|
|
||||||
lun_id = '1'
|
|
||||||
response = {'success': True, 'msg': 'Assign SAN client successfully'}
|
|
||||||
|
|
||||||
conf = {
|
|
||||||
'client.get_client_info.return_value': CLIENT_INFO,
|
|
||||||
}
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
|
|
||||||
self.driver.common._send_cmd_and_verify = mock.Mock(
|
|
||||||
return_value=response)
|
|
||||||
|
|
||||||
self.driver._get_lun_id = mock.Mock(return_value=lun_id)
|
|
||||||
|
|
||||||
result = self.driver._export_lun(VOLUME, CONNECTOR)
|
|
||||||
|
|
||||||
self.driver.common._send_cmd_and_verify.assert_called_with(
|
|
||||||
self.driver.common.vmem_mg.lun.assign_lun_to_client,
|
|
||||||
self.driver._is_lun_id_ready,
|
|
||||||
'Assign SAN client successfully',
|
|
||||||
[VOLUME['id'], CONNECTOR['host'], "ReadWrite"],
|
|
||||||
[VOLUME['id'], CONNECTOR['host']])
|
|
||||||
self.driver._get_lun_id.assert_called_with(
|
|
||||||
VOLUME['id'], CONNECTOR['host'])
|
|
||||||
self.assertEqual(lun_id, result)
|
|
||||||
|
|
||||||
def test_export_lun_fails_with_exception(self):
|
|
||||||
lun_id = '1'
|
|
||||||
response = {'status': False, 'msg': 'Generic error'}
|
|
||||||
failure = exception.ViolinBackendErr
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
self.driver.common._send_cmd_and_verify = mock.Mock(
|
|
||||||
side_effect=exception.ViolinBackendErr(response['msg']))
|
|
||||||
self.driver._get_lun_id = mock.Mock(return_value=lun_id)
|
|
||||||
|
|
||||||
self.assertRaises(failure, self.driver._export_lun, VOLUME, CONNECTOR)
|
|
||||||
|
|
||||||
def test_unexport_lun(self):
|
|
||||||
response = {'success': True, 'msg': 'Unassign SAN client successfully'}
|
|
||||||
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
self.driver.common._send_cmd = mock.Mock(
|
|
||||||
return_value=response)
|
|
||||||
|
|
||||||
result = self.driver._unexport_lun(VOLUME, CONNECTOR)
|
|
||||||
|
|
||||||
self.driver.common._send_cmd.assert_called_with(
|
|
||||||
self.driver.common.vmem_mg.lun.unassign_client_lun,
|
|
||||||
"Unassign SAN client successfully",
|
|
||||||
VOLUME['id'], CONNECTOR['host'], True)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_get_lun_id(self):
|
|
||||||
|
|
||||||
conf = {
|
|
||||||
'client.get_client_info.return_value': CLIENT_INFO,
|
|
||||||
}
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
|
|
||||||
result = self.driver._get_lun_id(VOLUME['id'], CONNECTOR['host'])
|
|
||||||
|
|
||||||
self.assertEqual(8, result)
|
|
||||||
|
|
||||||
def test_is_lun_id_ready(self):
|
|
||||||
lun_id = '1'
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto()
|
|
||||||
|
|
||||||
self.driver._get_lun_id = mock.Mock(return_value=lun_id)
|
|
||||||
|
|
||||||
result = self.driver._is_lun_id_ready(
|
|
||||||
VOLUME['id'], CONNECTOR['host'])
|
|
||||||
self.assertTrue(result)
|
|
||||||
|
|
||||||
def test_build_initiator_target_map(self):
|
|
||||||
"""Successfully build a map when zoning is enabled."""
|
|
||||||
expected_targ_wwns = FC_TARGET_WWPNS
|
|
||||||
|
|
||||||
self.driver.lookup_service = mock.Mock()
|
|
||||||
(self.driver.lookup_service.get_device_mapping_from_network.
|
|
||||||
return_value) = FC_FABRIC_MAP
|
|
||||||
|
|
||||||
result = self.driver._build_initiator_target_map(CONNECTOR)
|
|
||||||
(targ_wwns, init_targ_map) = result
|
|
||||||
|
|
||||||
(self.driver.lookup_service.get_device_mapping_from_network.
|
|
||||||
assert_called_with(CONNECTOR['wwpns'], self.driver.gateway_fc_wwns))
|
|
||||||
self.assertEqual(set(expected_targ_wwns), set(targ_wwns))
|
|
||||||
|
|
||||||
i = FC_INITIATOR_WWPNS[0]
|
|
||||||
self.assertIn(FC_TARGET_WWPNS[0], init_targ_map[i])
|
|
||||||
self.assertIn(FC_TARGET_WWPNS[1], init_targ_map[i])
|
|
||||||
self.assertEqual(2, len(init_targ_map[i]))
|
|
||||||
|
|
||||||
i = FC_INITIATOR_WWPNS[1]
|
|
||||||
self.assertIn(FC_TARGET_WWPNS[2], init_targ_map[i])
|
|
||||||
self.assertIn(FC_TARGET_WWPNS[3], init_targ_map[i])
|
|
||||||
self.assertEqual(2, len(init_targ_map[i]))
|
|
||||||
|
|
||||||
self.assertEqual(2, len(init_targ_map))
|
|
||||||
|
|
||||||
def test_build_initiator_target_map_no_lookup_service(self):
|
|
||||||
"""Successfully build a map when zoning is disabled."""
|
|
||||||
expected_targ_wwns = FC_TARGET_WWPNS
|
|
||||||
expected_init_targ_map = {
|
|
||||||
CONNECTOR['wwpns'][0]: FC_TARGET_WWPNS,
|
|
||||||
CONNECTOR['wwpns'][1]: FC_TARGET_WWPNS
|
|
||||||
}
|
|
||||||
self.driver.lookup_service = None
|
|
||||||
|
|
||||||
targ_wwns, init_targ_map = self.driver._build_initiator_target_map(
|
|
||||||
CONNECTOR)
|
|
||||||
|
|
||||||
self.assertEqual(expected_targ_wwns, targ_wwns)
|
|
||||||
self.assertEqual(expected_init_targ_map, init_targ_map)
|
|
||||||
|
|
||||||
def test_is_initiator_connected_to_array(self):
|
|
||||||
"""Successfully finds an initiator with remaining active session."""
|
|
||||||
conf = {
|
|
||||||
'client.get_client_info.return_value': CLIENT_INFO,
|
|
||||||
}
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
|
|
||||||
self.assertTrue(self.driver._is_initiator_connected_to_array(
|
|
||||||
CONNECTOR))
|
|
||||||
self.driver.common.vmem_mg.client.get_client_info.assert_called_with(
|
|
||||||
CONNECTOR['host'])
|
|
||||||
|
|
||||||
def test_is_initiator_connected_to_array_empty_response(self):
|
|
||||||
"""Successfully finds no initiators with remaining active sessions."""
|
|
||||||
conf = {
|
|
||||||
'client.get_client_info.return_value': CLIENT_INFO1
|
|
||||||
}
|
|
||||||
self.driver.common.vmem_mg = self.setup_mock_concerto(m_conf=conf)
|
|
||||||
|
|
||||||
self.assertFalse(self.driver._is_initiator_connected_to_array(
|
|
||||||
CONNECTOR))
|
|
File diff suppressed because it is too large
Load Diff
@ -1,395 +0,0 @@
|
|||||||
# Copyright 2015 Violin Memory, Inc.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Violin 7000 Series All-Flash Array Volume Driver
|
|
||||||
|
|
||||||
Provides fibre channel specific LUN services for V7000 series flash
|
|
||||||
arrays.
|
|
||||||
|
|
||||||
This driver requires Concerto v7.0.0 or newer software on the array.
|
|
||||||
|
|
||||||
You will need to install the Violin Memory REST client library:
|
|
||||||
sudo pip install vmemclient
|
|
||||||
|
|
||||||
Set the following in the cinder.conf file to enable the VMEM V7000
|
|
||||||
Fibre Channel Driver along with the required flags:
|
|
||||||
|
|
||||||
volume_driver=cinder.volume.drivers.violin.v7000_fcp.V7000FCDriver
|
|
||||||
|
|
||||||
NOTE: this driver file requires the use of synchronization points for
|
|
||||||
certain types of backend operations, and as a result may not work
|
|
||||||
properly in an active-active HA configuration. See OpenStack Cinder
|
|
||||||
driver documentation for more information.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
from cinder import exception
|
|
||||||
from cinder.i18n import _
|
|
||||||
from cinder import interface
|
|
||||||
from cinder import utils
|
|
||||||
from cinder.volume import driver
|
|
||||||
from cinder.volume.drivers.san import san
|
|
||||||
from cinder.volume.drivers.violin import v7000_common
|
|
||||||
from cinder.zonemanager import utils as fczm_utils
|
|
||||||
|
|
||||||
import socket
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@interface.volumedriver
|
|
||||||
class V7000FCPDriver(driver.FibreChannelDriver):
|
|
||||||
"""Executes commands relating to fibre channel based Violin Memory arrays.
|
|
||||||
|
|
||||||
Version history:
|
|
||||||
1.0 - Initial driver
|
|
||||||
"""
|
|
||||||
|
|
||||||
VERSION = '1.0'
|
|
||||||
|
|
||||||
# ThirdPartySystems wiki page
|
|
||||||
CI_WIKI_NAME = "Violin_Memory_CI"
|
|
||||||
|
|
||||||
# TODO(smcginnis) Either remove this if CI requirements are met, or
|
|
||||||
# remove this driver in the Queens release per normal deprecation
|
|
||||||
SUPPORTED = False
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(V7000FCPDriver, self).__init__(*args, **kwargs)
|
|
||||||
self.gateway_fc_wwns = []
|
|
||||||
self.stats = {}
|
|
||||||
self.configuration.append_config_values(v7000_common.violin_opts)
|
|
||||||
self.configuration.append_config_values(san.san_opts)
|
|
||||||
self.common = v7000_common.V7000Common(self.configuration)
|
|
||||||
self.lookup_service = fczm_utils.create_lookup_service()
|
|
||||||
|
|
||||||
LOG.info("Initialized driver %(name)s version: %(vers)s",
|
|
||||||
{'name': self.__class__.__name__, 'vers': self.VERSION})
|
|
||||||
|
|
||||||
def do_setup(self, context):
|
|
||||||
"""Any initialization the driver does while starting."""
|
|
||||||
super(V7000FCPDriver, self).do_setup(context)
|
|
||||||
|
|
||||||
self.common.do_setup(context)
|
|
||||||
self.gateway_fc_wwns = self._get_active_fc_targets()
|
|
||||||
|
|
||||||
# Register the client with the storage array
|
|
||||||
fc_version = self.VERSION + "-FCP"
|
|
||||||
self.common.vmem_mg.utility.set_managed_by_openstack_version(
|
|
||||||
fc_version)
|
|
||||||
|
|
||||||
def check_for_setup_error(self):
|
|
||||||
"""Returns an error if prerequisites aren't met."""
|
|
||||||
self.common.check_for_setup_error()
|
|
||||||
if len(self.gateway_fc_wwns) == 0:
|
|
||||||
raise exception.ViolinInvalidBackendConfig(
|
|
||||||
reason=_('No FCP targets found'))
|
|
||||||
|
|
||||||
def create_volume(self, volume):
|
|
||||||
"""Creates a volume."""
|
|
||||||
self.common._create_lun(volume)
|
|
||||||
|
|
||||||
def create_volume_from_snapshot(self, volume, snapshot):
|
|
||||||
"""Creates a volume from a snapshot."""
|
|
||||||
self.common._create_volume_from_snapshot(snapshot, volume)
|
|
||||||
|
|
||||||
def create_cloned_volume(self, volume, src_vref):
|
|
||||||
"""Creates a clone of the specified volume."""
|
|
||||||
self.common._create_lun_from_lun(src_vref, volume)
|
|
||||||
|
|
||||||
def delete_volume(self, volume):
|
|
||||||
"""Deletes a volume."""
|
|
||||||
self.common._delete_lun(volume)
|
|
||||||
|
|
||||||
def extend_volume(self, volume, new_size):
|
|
||||||
"""Extend an existing volume's size."""
|
|
||||||
self.common._extend_lun(volume, new_size)
|
|
||||||
|
|
||||||
def create_snapshot(self, snapshot):
|
|
||||||
"""Creates a snapshot."""
|
|
||||||
self.common._create_lun_snapshot(snapshot)
|
|
||||||
|
|
||||||
def delete_snapshot(self, snapshot):
|
|
||||||
"""Deletes a snapshot."""
|
|
||||||
self.common._delete_lun_snapshot(snapshot)
|
|
||||||
|
|
||||||
def ensure_export(self, context, volume):
|
|
||||||
"""Synchronously checks and re-exports volumes at cinder start time."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_export(self, context, volume, connector):
|
|
||||||
"""Exports the volume."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def remove_export(self, context, volume):
|
|
||||||
"""Removes an export for a logical volume."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@fczm_utils.add_fc_zone
|
|
||||||
def initialize_connection(self, volume, connector):
|
|
||||||
"""Allow connection to connector and return connection info."""
|
|
||||||
|
|
||||||
LOG.debug("Initialize_connection: initiator - %(initiator)s host - "
|
|
||||||
"%(host)s wwpns - %(wwpns)s",
|
|
||||||
{'initiator': connector['initiator'],
|
|
||||||
'host': connector['host'],
|
|
||||||
'wwpns': connector['wwpns']})
|
|
||||||
|
|
||||||
self.common.vmem_mg.client.create_client(
|
|
||||||
name=connector['host'], proto='FC', fc_wwns=connector['wwpns'])
|
|
||||||
|
|
||||||
lun_id = self._export_lun(volume, connector)
|
|
||||||
|
|
||||||
target_wwns, init_targ_map = self._build_initiator_target_map(
|
|
||||||
connector)
|
|
||||||
|
|
||||||
properties = {}
|
|
||||||
properties['target_discovered'] = True
|
|
||||||
properties['target_wwn'] = target_wwns
|
|
||||||
properties['target_lun'] = lun_id
|
|
||||||
properties['initiator_target_map'] = init_targ_map
|
|
||||||
|
|
||||||
LOG.debug("Return FC data for zone addition: %(properties)s.",
|
|
||||||
{'properties': properties})
|
|
||||||
|
|
||||||
return {'driver_volume_type': 'fibre_channel', 'data': properties}
|
|
||||||
|
|
||||||
@fczm_utils.remove_fc_zone
|
|
||||||
def terminate_connection(self, volume, connector, **kwargs):
|
|
||||||
"""Terminates the connection (target<-->initiator)."""
|
|
||||||
|
|
||||||
self._unexport_lun(volume, connector)
|
|
||||||
|
|
||||||
properties = {}
|
|
||||||
|
|
||||||
if not self._is_initiator_connected_to_array(connector):
|
|
||||||
target_wwns, init_targ_map = self._build_initiator_target_map(
|
|
||||||
connector)
|
|
||||||
properties['target_wwn'] = target_wwns
|
|
||||||
properties['initiator_target_map'] = init_targ_map
|
|
||||||
|
|
||||||
LOG.debug("Return FC data for zone deletion: %(properties)s.",
|
|
||||||
{'properties': properties})
|
|
||||||
|
|
||||||
return {'driver_volume_type': 'fibre_channel', 'data': properties}
|
|
||||||
|
|
||||||
def get_volume_stats(self, refresh=False):
|
|
||||||
"""Get volume stats.
|
|
||||||
|
|
||||||
If 'refresh' is True, update the stats first.
|
|
||||||
"""
|
|
||||||
if refresh or not self.stats:
|
|
||||||
self._update_volume_stats()
|
|
||||||
return self.stats
|
|
||||||
|
|
||||||
@utils.synchronized('vmem-export')
|
|
||||||
def _export_lun(self, volume, connector=None):
|
|
||||||
"""Generates the export configuration for the given volume.
|
|
||||||
|
|
||||||
:param volume: volume object provided by the Manager
|
|
||||||
:param connector: connector object provided by the Manager
|
|
||||||
:returns: the LUN ID assigned by the backend
|
|
||||||
"""
|
|
||||||
lun_id = ''
|
|
||||||
v = self.common.vmem_mg
|
|
||||||
|
|
||||||
if not connector:
|
|
||||||
raise exception.ViolinInvalidBackendConfig(
|
|
||||||
reason=_('No initiators found, cannot proceed'))
|
|
||||||
|
|
||||||
LOG.debug("Exporting lun %(vol_id)s - initiator wwpns %(i_wwpns)s "
|
|
||||||
"- target wwpns %(t_wwpns)s.",
|
|
||||||
{'vol_id': volume['id'], 'i_wwpns': connector['wwpns'],
|
|
||||||
't_wwpns': self.gateway_fc_wwns})
|
|
||||||
|
|
||||||
try:
|
|
||||||
lun_id = self.common._send_cmd_and_verify(
|
|
||||||
v.lun.assign_lun_to_client,
|
|
||||||
self._is_lun_id_ready,
|
|
||||||
"Assign SAN client successfully",
|
|
||||||
[volume['id'], connector['host'],
|
|
||||||
"ReadWrite"],
|
|
||||||
[volume['id'], connector['host']])
|
|
||||||
|
|
||||||
except exception.ViolinBackendErr:
|
|
||||||
LOG.exception("Backend returned err for lun export.")
|
|
||||||
raise
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
raise exception.ViolinInvalidBackendConfig(
|
|
||||||
reason=_('LUN export failed!'))
|
|
||||||
|
|
||||||
lun_id = self._get_lun_id(volume['id'], connector['host'])
|
|
||||||
LOG.info("Exported lun %(vol_id)s on lun_id %(lun_id)s.",
|
|
||||||
{'vol_id': volume['id'], 'lun_id': lun_id})
|
|
||||||
|
|
||||||
return lun_id
|
|
||||||
|
|
||||||
@utils.synchronized('vmem-export')
|
|
||||||
def _unexport_lun(self, volume, connector=None):
|
|
||||||
"""Removes the export configuration for the given volume.
|
|
||||||
|
|
||||||
:param volume: volume object provided by the Manager
|
|
||||||
"""
|
|
||||||
v = self.common.vmem_mg
|
|
||||||
|
|
||||||
LOG.info("Unexporting lun %s.", volume['id'])
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.common._send_cmd(v.lun.unassign_client_lun,
|
|
||||||
"Unassign SAN client successfully",
|
|
||||||
volume['id'], connector['host'], True)
|
|
||||||
|
|
||||||
except exception.ViolinBackendErr:
|
|
||||||
LOG.exception("Backend returned err for lun export.")
|
|
||||||
raise
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
LOG.exception("LUN unexport failed!")
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _update_volume_stats(self):
|
|
||||||
"""Gathers array stats and converts them to GB values."""
|
|
||||||
data = {}
|
|
||||||
total_gb = 0
|
|
||||||
free_gb = 0
|
|
||||||
v = self.common.vmem_mg.basic
|
|
||||||
array_name_triple = socket.gethostbyaddr(self.configuration.san_ip)
|
|
||||||
array_name = array_name_triple[0]
|
|
||||||
|
|
||||||
phy_devices = v.get("/batch/physicalresource/physicaldevice")
|
|
||||||
|
|
||||||
all_devices = [x for x in phy_devices['data']['physical_devices']]
|
|
||||||
|
|
||||||
for x in all_devices:
|
|
||||||
if socket.getfqdn(x['owner']) == array_name:
|
|
||||||
total_gb += x['size_mb'] // 1024
|
|
||||||
free_gb += x['availsize_mb'] // 1024
|
|
||||||
|
|
||||||
backend_name = self.configuration.volume_backend_name
|
|
||||||
data['volume_backend_name'] = backend_name or self.__class__.__name__
|
|
||||||
data['vendor_name'] = 'Violin Memory, Inc.'
|
|
||||||
data['driver_version'] = self.VERSION
|
|
||||||
data['storage_protocol'] = 'fibre_channel'
|
|
||||||
data['reserved_percentage'] = 0
|
|
||||||
data['QoS_support'] = False
|
|
||||||
data['total_capacity_gb'] = total_gb
|
|
||||||
data['free_capacity_gb'] = free_gb
|
|
||||||
for i in data:
|
|
||||||
LOG.debug("stat update: %(name)s=%(data)s",
|
|
||||||
{'name': i, 'data': data[i]})
|
|
||||||
|
|
||||||
self.stats = data
|
|
||||||
|
|
||||||
def _get_active_fc_targets(self):
|
|
||||||
"""Get a list of gateway WWNs that can be used as FCP targets.
|
|
||||||
|
|
||||||
:param mg_conn: active XG connection to one of the gateways
|
|
||||||
:returns: list of WWNs in openstack format
|
|
||||||
"""
|
|
||||||
v = self.common.vmem_mg
|
|
||||||
active_gw_fcp_wwns = []
|
|
||||||
|
|
||||||
fc_info = v.adapter.get_fc_info()
|
|
||||||
for x in fc_info.values():
|
|
||||||
active_gw_fcp_wwns.append(x[0])
|
|
||||||
|
|
||||||
return active_gw_fcp_wwns
|
|
||||||
|
|
||||||
def _get_lun_id(self, volume_name, client_name):
|
|
||||||
"""Get the lun ID for an exported volume.
|
|
||||||
|
|
||||||
If the lun is successfully assigned (exported) to a client, the
|
|
||||||
client info has the lun_id.
|
|
||||||
|
|
||||||
:param volume_name: name of volume to query for lun ID
|
|
||||||
:param client_name: name of client associated with the volume
|
|
||||||
:returns: integer value of lun ID
|
|
||||||
"""
|
|
||||||
v = self.common.vmem_mg
|
|
||||||
lun_id = None
|
|
||||||
|
|
||||||
client_info = v.client.get_client_info(client_name)
|
|
||||||
|
|
||||||
for x in client_info['FibreChannelDevices']:
|
|
||||||
if volume_name == x['name']:
|
|
||||||
lun_id = x['lun']
|
|
||||||
break
|
|
||||||
|
|
||||||
if lun_id:
|
|
||||||
lun_id = int(lun_id)
|
|
||||||
|
|
||||||
return lun_id
|
|
||||||
|
|
||||||
def _is_lun_id_ready(self, volume_name, client_name):
|
|
||||||
"""Get the lun ID for an exported volume.
|
|
||||||
|
|
||||||
If the lun is successfully assigned (exported) to a client, the
|
|
||||||
client info has the lun_id.
|
|
||||||
|
|
||||||
:param volume_name: name of volume to query for lun ID
|
|
||||||
:param client_name: name of client associated with the volume
|
|
||||||
:returns: Returns True if lun is ready, False otherwise
|
|
||||||
"""
|
|
||||||
|
|
||||||
lun_id = -1
|
|
||||||
lun_id = self._get_lun_id(volume_name, client_name)
|
|
||||||
if lun_id is None:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _build_initiator_target_map(self, connector):
|
|
||||||
"""Build the target_wwns and the initiator target map."""
|
|
||||||
target_wwns = []
|
|
||||||
init_targ_map = {}
|
|
||||||
|
|
||||||
if self.lookup_service:
|
|
||||||
dev_map = self.lookup_service.get_device_mapping_from_network(
|
|
||||||
connector['wwpns'], self.gateway_fc_wwns)
|
|
||||||
|
|
||||||
for fabric_name in dev_map:
|
|
||||||
fabric = dev_map[fabric_name]
|
|
||||||
target_wwns += fabric['target_port_wwn_list']
|
|
||||||
for initiator in fabric['initiator_port_wwn_list']:
|
|
||||||
if initiator not in init_targ_map:
|
|
||||||
init_targ_map[initiator] = []
|
|
||||||
init_targ_map[initiator] += fabric['target_port_wwn_list']
|
|
||||||
init_targ_map[initiator] = list(
|
|
||||||
set(init_targ_map[initiator]))
|
|
||||||
|
|
||||||
target_wwns = list(set(target_wwns))
|
|
||||||
|
|
||||||
else:
|
|
||||||
initiator_wwns = connector['wwpns']
|
|
||||||
target_wwns = self.gateway_fc_wwns
|
|
||||||
for initiator in initiator_wwns:
|
|
||||||
init_targ_map[initiator] = target_wwns
|
|
||||||
|
|
||||||
return target_wwns, init_targ_map
|
|
||||||
|
|
||||||
def _is_initiator_connected_to_array(self, connector):
|
|
||||||
"""Check if any initiator wwns still have active sessions."""
|
|
||||||
v = self.common.vmem_mg
|
|
||||||
|
|
||||||
client = v.client.get_client_info(connector['host'])
|
|
||||||
|
|
||||||
if len(client['FibreChannelDevices']):
|
|
||||||
# each entry in the FibreChannelDevices array is a dict
|
|
||||||
# describing an active lun assignment
|
|
||||||
return True
|
|
||||||
return False
|
|
@ -1,352 +0,0 @@
|
|||||||
# Copyright 2016 Violin Memory, Inc.
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Violin 7000 Series All-Flash Array iSCSI Volume Driver
|
|
||||||
|
|
||||||
Provides ISCSI specific LUN services for V7000 series flash arrays.
|
|
||||||
|
|
||||||
This driver requires Concerto v7.5.4 or newer software on the array.
|
|
||||||
|
|
||||||
You will need to install the python VMEM REST client:
|
|
||||||
sudo pip install vmemclient
|
|
||||||
|
|
||||||
Set the following in the cinder.conf file to enable the VMEM V7000
|
|
||||||
ISCSI Driver along with the required flags:
|
|
||||||
|
|
||||||
volume_driver=cinder.volume.drivers.violin.v7000_iscsi.V7000ISCSIDriver
|
|
||||||
"""
|
|
||||||
|
|
||||||
import random
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
from cinder import exception
|
|
||||||
from cinder.i18n import _
|
|
||||||
from cinder import interface
|
|
||||||
from cinder.volume import driver
|
|
||||||
from cinder.volume.drivers.san import san
|
|
||||||
from cinder.volume.drivers.violin import v7000_common
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@interface.volumedriver
|
|
||||||
class V7000ISCSIDriver(driver.ISCSIDriver):
|
|
||||||
"""Executes commands relating to iscsi based Violin Memory arrays.
|
|
||||||
|
|
||||||
Version history:
|
|
||||||
1.0 - Initial driver
|
|
||||||
"""
|
|
||||||
|
|
||||||
VERSION = '1.0'
|
|
||||||
|
|
||||||
# ThirdPartySystems wiki page
|
|
||||||
CI_WIKI_NAME = "Violin_Memory_CI"
|
|
||||||
|
|
||||||
# TODO(smcginnis) Either remove this if CI requirements are met, or
|
|
||||||
# remove this driver in the Queens release per normal deprecation
|
|
||||||
SUPPORTED = False
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(V7000ISCSIDriver, self).__init__(*args, **kwargs)
|
|
||||||
self.stats = {}
|
|
||||||
self.gateway_iscsi_ip_addresses = []
|
|
||||||
self.configuration.append_config_values(v7000_common.violin_opts)
|
|
||||||
self.configuration.append_config_values(san.san_opts)
|
|
||||||
self.common = v7000_common.V7000Common(self.configuration)
|
|
||||||
|
|
||||||
LOG.info("Initialized driver %(name)s version: %(vers)s",
|
|
||||||
{'name': self.__class__.__name__, 'vers': self.VERSION})
|
|
||||||
|
|
||||||
def do_setup(self, context):
|
|
||||||
"""Any initialization the driver does while starting."""
|
|
||||||
super(V7000ISCSIDriver, self).do_setup(context)
|
|
||||||
|
|
||||||
self.common.do_setup(context)
|
|
||||||
|
|
||||||
# Register the client with the storage array
|
|
||||||
iscsi_version = self.VERSION + "-ISCSI"
|
|
||||||
self.common.vmem_mg.utility.set_managed_by_openstack_version(
|
|
||||||
iscsi_version, protocol="iSCSI")
|
|
||||||
|
|
||||||
# Getting iscsi IPs from the array is incredibly expensive,
|
|
||||||
# so only do it once.
|
|
||||||
if not self.configuration.violin_iscsi_target_ips:
|
|
||||||
LOG.warning("iSCSI target ip addresses not configured.")
|
|
||||||
self.gateway_iscsi_ip_addresses = (
|
|
||||||
self.common.vmem_mg.utility.get_iscsi_interfaces())
|
|
||||||
else:
|
|
||||||
self.gateway_iscsi_ip_addresses = (
|
|
||||||
self.configuration.violin_iscsi_target_ips)
|
|
||||||
|
|
||||||
def check_for_setup_error(self):
|
|
||||||
"""Returns an error if prerequisites aren't met."""
|
|
||||||
self.common.check_for_setup_error()
|
|
||||||
if len(self.gateway_iscsi_ip_addresses) == 0:
|
|
||||||
msg = _('No iSCSI IPs configured on SAN gateway')
|
|
||||||
raise exception.ViolinInvalidBackendConfig(reason=msg)
|
|
||||||
|
|
||||||
def create_volume(self, volume):
|
|
||||||
"""Creates a volume."""
|
|
||||||
self.common._create_lun(volume)
|
|
||||||
|
|
||||||
def create_volume_from_snapshot(self, volume, snapshot):
|
|
||||||
"""Creates a volume from a snapshot."""
|
|
||||||
self.common._create_volume_from_snapshot(snapshot, volume)
|
|
||||||
|
|
||||||
def create_cloned_volume(self, volume, src_vref):
|
|
||||||
"""Creates a clone of the specified volume."""
|
|
||||||
self.common._create_lun_from_lun(src_vref, volume)
|
|
||||||
|
|
||||||
def delete_volume(self, volume):
|
|
||||||
"""Deletes a volume."""
|
|
||||||
self.common._delete_lun(volume)
|
|
||||||
|
|
||||||
def extend_volume(self, volume, new_size):
|
|
||||||
"""Extend an existing volume's size."""
|
|
||||||
self.common._extend_lun(volume, new_size)
|
|
||||||
|
|
||||||
def create_snapshot(self, snapshot):
|
|
||||||
"""Creates a snapshot."""
|
|
||||||
self.common._create_lun_snapshot(snapshot)
|
|
||||||
|
|
||||||
def delete_snapshot(self, snapshot):
|
|
||||||
"""Deletes a snapshot."""
|
|
||||||
self.common._delete_lun_snapshot(snapshot)
|
|
||||||
|
|
||||||
def ensure_export(self, context, volume):
|
|
||||||
"""Synchronously checks and re-exports volumes at cinder start time."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_export(self, context, volume, connector):
|
|
||||||
"""Exports the volume."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def remove_export(self, context, volume):
|
|
||||||
"""Removes an export for a logical volume."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def initialize_connection(self, volume, connector):
|
|
||||||
"""Allow connection to connector and return connection info."""
|
|
||||||
resp = {}
|
|
||||||
|
|
||||||
LOG.debug("Initialize_connection: initiator - %(initiator)s host - "
|
|
||||||
"%(host)s ip - %(ip)s",
|
|
||||||
{'initiator': connector['initiator'],
|
|
||||||
'host': connector['host'],
|
|
||||||
'ip': connector['ip']})
|
|
||||||
|
|
||||||
iqn = self._get_iqn(connector)
|
|
||||||
|
|
||||||
# pick a random single target to give the connector since
|
|
||||||
# there is no multipathing support
|
|
||||||
tgt = self.gateway_iscsi_ip_addresses[random.randint(
|
|
||||||
0, len(self.gateway_iscsi_ip_addresses) - 1)]
|
|
||||||
|
|
||||||
resp = self.common.vmem_mg.client.create_client(
|
|
||||||
name=connector['host'], proto='iSCSI',
|
|
||||||
iscsi_iqns=connector['initiator'])
|
|
||||||
|
|
||||||
# raise if we failed for any reason other than a 'client
|
|
||||||
# already exists' error code
|
|
||||||
if not resp['success'] and 'Error: 0x900100cd' not in resp['msg']:
|
|
||||||
msg = _("Failed to create iscsi client")
|
|
||||||
raise exception.ViolinBackendErr(message=msg)
|
|
||||||
|
|
||||||
resp = self.common.vmem_mg.client.create_iscsi_target(
|
|
||||||
name=iqn, client_name=connector['host'],
|
|
||||||
ip=self.gateway_iscsi_ip_addresses, access_mode='ReadWrite')
|
|
||||||
|
|
||||||
# same here, raise for any failure other than a 'target
|
|
||||||
# already exists' error code
|
|
||||||
if not resp['success'] and 'Error: 0x09024309' not in resp['msg']:
|
|
||||||
msg = _("Failed to create iscsi target")
|
|
||||||
raise exception.ViolinBackendErr(message=msg)
|
|
||||||
|
|
||||||
lun_id = self._export_lun(volume, iqn, connector)
|
|
||||||
|
|
||||||
properties = {}
|
|
||||||
properties['target_discovered'] = False
|
|
||||||
properties['target_iqn'] = iqn
|
|
||||||
properties['target_portal'] = '%s:%s' % (tgt, '3260')
|
|
||||||
properties['target_lun'] = lun_id
|
|
||||||
properties['volume_id'] = volume['id']
|
|
||||||
|
|
||||||
LOG.debug("Return ISCSI data: %(properties)s.",
|
|
||||||
{'properties': properties})
|
|
||||||
|
|
||||||
return {'driver_volume_type': 'iscsi', 'data': properties}
|
|
||||||
|
|
||||||
def terminate_connection(self, volume, connector, **kwargs):
|
|
||||||
"""Terminates the connection (target<-->initiator)."""
|
|
||||||
iqn = self._get_iqn(connector)
|
|
||||||
self._unexport_lun(volume, iqn, connector)
|
|
||||||
|
|
||||||
def get_volume_stats(self, refresh=False):
|
|
||||||
"""Get volume stats.
|
|
||||||
|
|
||||||
If 'refresh' is True, update the stats first.
|
|
||||||
"""
|
|
||||||
if refresh or not self.stats:
|
|
||||||
self._update_volume_stats()
|
|
||||||
return self.stats
|
|
||||||
|
|
||||||
def _update_volume_stats(self):
|
|
||||||
"""Gathers array stats and converts them to GB values."""
|
|
||||||
data = self.common._get_volume_stats(self.configuration.san_ip)
|
|
||||||
|
|
||||||
backend_name = self.configuration.volume_backend_name
|
|
||||||
data['volume_backend_name'] = backend_name or self.__class__.__name__
|
|
||||||
data['driver_version'] = self.VERSION
|
|
||||||
data['storage_protocol'] = 'iSCSI'
|
|
||||||
for i in data:
|
|
||||||
LOG.debug("stat update: %(name)s=%(data)s",
|
|
||||||
{'name': i, 'data': data[i]})
|
|
||||||
|
|
||||||
self.stats = data
|
|
||||||
|
|
||||||
def _export_lun(self, volume, target, connector):
|
|
||||||
"""Generates the export configuration for the given volume.
|
|
||||||
|
|
||||||
:param volume: volume object provided by the Manager
|
|
||||||
:param connector: connector object provided by the Manager
|
|
||||||
:returns: the LUN ID assigned by the backend
|
|
||||||
"""
|
|
||||||
lun_id = ''
|
|
||||||
v = self.common.vmem_mg
|
|
||||||
|
|
||||||
LOG.debug("Exporting lun %(vol_id)s - initiator iqns %(i_iqns)s "
|
|
||||||
"- target iqns %(t_iqns)s.",
|
|
||||||
{'vol_id': volume['id'], 'i_iqns': connector['initiator'],
|
|
||||||
't_iqns': self.gateway_iscsi_ip_addresses})
|
|
||||||
|
|
||||||
try:
|
|
||||||
lun_id = self.common._send_cmd_and_verify(
|
|
||||||
v.lun.assign_lun_to_iscsi_target,
|
|
||||||
self._is_lun_id_ready,
|
|
||||||
"Assign device successfully",
|
|
||||||
[volume['id'], target],
|
|
||||||
[volume['id'], connector['host']])
|
|
||||||
|
|
||||||
except exception.ViolinBackendErr:
|
|
||||||
LOG.exception("Backend returned error for lun export.")
|
|
||||||
raise
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
raise exception.ViolinInvalidBackendConfig(
|
|
||||||
reason=_('LUN export failed!'))
|
|
||||||
|
|
||||||
lun_id = self._get_lun_id(volume['id'], connector['host'])
|
|
||||||
LOG.info("Exported lun %(vol_id)s on lun_id %(lun_id)s.",
|
|
||||||
{'vol_id': volume['id'], 'lun_id': lun_id})
|
|
||||||
|
|
||||||
return lun_id
|
|
||||||
|
|
||||||
def _unexport_lun(self, volume, target, connector):
|
|
||||||
"""Removes the export configuration for the given volume.
|
|
||||||
|
|
||||||
The equivalent CLI command is "no lun export container
|
|
||||||
<container_name> name <lun_name>"
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
volume -- volume object provided by the Manager
|
|
||||||
"""
|
|
||||||
v = self.common.vmem_mg
|
|
||||||
|
|
||||||
LOG.info("Unexporting lun %(vol)s host is %(host)s.",
|
|
||||||
{'vol': volume['id'], 'host': connector['host']})
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.common._send_cmd(v.lun.unassign_lun_from_iscsi_target,
|
|
||||||
"Unassign device successfully",
|
|
||||||
volume['id'], target, True)
|
|
||||||
|
|
||||||
except exception.ViolinBackendErrNotFound:
|
|
||||||
LOG.info("Lun %s already unexported, continuing...",
|
|
||||||
volume['id'])
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
LOG.exception("LUN unexport failed!")
|
|
||||||
msg = _("LUN unexport failed")
|
|
||||||
raise exception.ViolinBackendErr(message=msg)
|
|
||||||
|
|
||||||
def _is_lun_id_ready(self, volume_name, client_name):
|
|
||||||
"""Get the lun ID for an exported volume.
|
|
||||||
|
|
||||||
If the lun is successfully assigned (exported) to a client, the
|
|
||||||
client info has the lun_id.
|
|
||||||
|
|
||||||
Note: The structure returned for iscsi is different from the
|
|
||||||
one returned for FC. Therefore this function is here instead of
|
|
||||||
common.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
volume_name -- name of volume to query for lun ID
|
|
||||||
client_name -- name of client associated with the volume
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
lun_id -- Returns True or False
|
|
||||||
"""
|
|
||||||
|
|
||||||
lun_id = -1
|
|
||||||
lun_id = self._get_lun_id(volume_name, client_name)
|
|
||||||
|
|
||||||
if lun_id is None:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _get_lun_id(self, volume_name, client_name):
|
|
||||||
"""Get the lun ID for an exported volume.
|
|
||||||
|
|
||||||
If the lun is successfully assigned (exported) to a client, the
|
|
||||||
client info has the lun_id.
|
|
||||||
|
|
||||||
Note: The structure returned for iscsi is different from the
|
|
||||||
one returned for FC. Therefore this function is here instead of
|
|
||||||
common.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
volume_name -- name of volume to query for lun ID
|
|
||||||
client_name -- name of client associated with the volume
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
lun_id -- integer value of lun ID
|
|
||||||
"""
|
|
||||||
v = self.common.vmem_mg
|
|
||||||
lun_id = None
|
|
||||||
|
|
||||||
client_info = v.client.get_client_info(client_name)
|
|
||||||
|
|
||||||
for x in client_info['ISCSIDevices']:
|
|
||||||
if volume_name == x['name']:
|
|
||||||
lun_id = x['lun']
|
|
||||||
break
|
|
||||||
|
|
||||||
if lun_id:
|
|
||||||
lun_id = int(lun_id)
|
|
||||||
|
|
||||||
return lun_id
|
|
||||||
|
|
||||||
def _get_iqn(self, connector):
|
|
||||||
# The vmemclient connection properties list hostname field may
|
|
||||||
# change depending on failover cluster config. Use a UUID
|
|
||||||
# from the backend's IP address to avoid a potential naming
|
|
||||||
# issue.
|
|
||||||
host_uuid = uuid.uuid3(uuid.NAMESPACE_DNS, self.configuration.san_ip)
|
|
||||||
return "%s%s.%s" % (self.configuration.iscsi_target_prefix,
|
|
||||||
connector['host'], host_uuid)
|
|
@ -1,107 +0,0 @@
|
|||||||
===========================================
|
|
||||||
Violin Memory 7000 Series FSP volume driver
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
The OpenStack V7000 driver package from Violin Memory adds Block Storage
|
|
||||||
service support for Violin 7300 Flash Storage Platforms (FSPs) and 7700 FSP
|
|
||||||
controllers.
|
|
||||||
|
|
||||||
The driver package release can be used with any OpenStack Liberty deployment
|
|
||||||
for all 7300 FSPs and 7700 FSP controllers running Concerto 7.5.3 and later
|
|
||||||
using Fibre Channel HBAs.
|
|
||||||
|
|
||||||
System requirements
|
|
||||||
~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
To use the Violin driver, the following are required:
|
|
||||||
|
|
||||||
- Violin 7300/7700 series FSP with:
|
|
||||||
|
|
||||||
- Concerto OS version 7.5.3 or later
|
|
||||||
|
|
||||||
- Fibre channel host interfaces
|
|
||||||
|
|
||||||
- The Violin block storage driver: This driver implements the block storage API
|
|
||||||
calls. The driver is included with the OpenStack Liberty release.
|
|
||||||
|
|
||||||
- The vmemclient library: This is the Violin Array Communications library to
|
|
||||||
the Flash Storage Platform through a REST-like interface. The client can be
|
|
||||||
installed using the python 'pip' installer tool. Further information on
|
|
||||||
vmemclient can be found on `PyPI
|
|
||||||
<https://pypi.python.org/pypi/vmemclient/>`__.
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
pip install vmemclient
|
|
||||||
|
|
||||||
Supported operations
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- Create, delete, attach, and detach volumes.
|
|
||||||
|
|
||||||
- Create, list, and delete volume snapshots.
|
|
||||||
|
|
||||||
- Create a volume from a snapshot.
|
|
||||||
|
|
||||||
- Copy an image to a volume.
|
|
||||||
|
|
||||||
- Copy a volume to an image.
|
|
||||||
|
|
||||||
- Clone a volume.
|
|
||||||
|
|
||||||
- Extend a volume.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Listed operations are supported for thick, thin, and dedup luns,
|
|
||||||
with the exception of cloning. Cloning operations are supported only
|
|
||||||
on thick luns.
|
|
||||||
|
|
||||||
Driver configuration
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Once the array is configured as per the installation guide, it is simply a
|
|
||||||
matter of editing the cinder configuration file to add or modify the
|
|
||||||
parameters. The driver currently only supports fibre channel configuration.
|
|
||||||
|
|
||||||
Fibre channel configuration
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Set the following in your ``cinder.conf`` configuration file, replacing the
|
|
||||||
variables using the guide in the following section:
|
|
||||||
|
|
||||||
.. code-block:: ini
|
|
||||||
|
|
||||||
volume_driver = cinder.volume.drivers.violin.v7000_fcp.V7000FCPDriver
|
|
||||||
volume_backend_name = vmem_violinfsp
|
|
||||||
extra_capabilities = VMEM_CAPABILITIES
|
|
||||||
san_ip = VMEM_MGMT_IP
|
|
||||||
san_login = VMEM_USER_NAME
|
|
||||||
san_password = VMEM_PASSWORD
|
|
||||||
use_multipath_for_image_xfer = true
|
|
||||||
|
|
||||||
Configuration parameters
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Description of configuration value placeholders:
|
|
||||||
|
|
||||||
VMEM_CAPABILITIES
|
|
||||||
User defined capabilities, a JSON formatted string specifying key-value
|
|
||||||
pairs (string value). The ones particularly supported are
|
|
||||||
``dedup`` and ``thin``. Only these two capabilities are listed here in
|
|
||||||
``cinder.conf`` file, indicating this backend be selected for creating
|
|
||||||
luns which have a volume type associated with them that have ``dedup``
|
|
||||||
or ``thin`` extra_specs specified. For example, if the FSP is configured
|
|
||||||
to support dedup luns, set the associated driver capabilities
|
|
||||||
to: {"dedup":"True","thin":"True"}.
|
|
||||||
|
|
||||||
VMEM_MGMT_IP
|
|
||||||
External IP address or host name of the Violin 7300 Memory Gateway. This
|
|
||||||
can be an IP address or host name.
|
|
||||||
|
|
||||||
VMEM_USER_NAME
|
|
||||||
Log-in user name for the Violin 7300 Memory Gateway or 7700 FSP controller.
|
|
||||||
This user must have administrative rights on the array or controller.
|
|
||||||
|
|
||||||
VMEM_PASSWORD
|
|
||||||
Log-in user's password.
|
|
@ -52,7 +52,6 @@ Volume drivers
|
|||||||
drivers/solidfire-volume-driver.rst
|
drivers/solidfire-volume-driver.rst
|
||||||
drivers/synology-dsm-driver.rst
|
drivers/synology-dsm-driver.rst
|
||||||
drivers/tintri-volume-driver.rst
|
drivers/tintri-volume-driver.rst
|
||||||
drivers/violin-v7000-driver.rst
|
|
||||||
drivers/vzstorage-driver.rst
|
drivers/vzstorage-driver.rst
|
||||||
drivers/vmware-vmdk-driver.rst
|
drivers/vmware-vmdk-driver.rst
|
||||||
drivers/windows-iscsi-volume-driver.rst
|
drivers/windows-iscsi-volume-driver.rst
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
..
|
|
||||||
Warning: Do not edit this file. It is automatically generated from the
|
|
||||||
software project's code and your changes will be overwritten.
|
|
||||||
|
|
||||||
The tool to generate this file lives in openstack-doc-tools repository.
|
|
||||||
|
|
||||||
Please make any changes needed in the code, then run the
|
|
||||||
autogenerate-config-doc tool from the openstack-doc-tools repository, or
|
|
||||||
ask for help on the documentation mailing list, IRC channel or meeting.
|
|
||||||
|
|
||||||
.. _cinder-violin:
|
|
||||||
|
|
||||||
.. list-table:: Description of Violin volume driver configuration options
|
|
||||||
:header-rows: 1
|
|
||||||
:class: config-ref-table
|
|
||||||
|
|
||||||
* - Configuration option = Default value
|
|
||||||
- Description
|
|
||||||
* - **[DEFAULT]**
|
|
||||||
-
|
|
||||||
* - ``violin_dedup_capable_pools`` =
|
|
||||||
- (List) Storage pools capable of dedup and other luns.(Comma separated list)
|
|
||||||
* - ``violin_dedup_only_pools`` =
|
|
||||||
- (List) Storage pools to be used to setup dedup luns only.(Comma separated list)
|
|
||||||
* - ``violin_iscsi_target_ips`` =
|
|
||||||
- (List) Target iSCSI addresses to use.(Comma separated list)
|
|
||||||
* - ``violin_pool_allocation_method`` = ``random``
|
|
||||||
- (String) Method of choosing a storage pool for a lun.
|
|
||||||
* - ``violin_request_timeout`` = ``300``
|
|
||||||
- (Integer) Global backend request timeout, in seconds.
|
|
@ -11,4 +11,5 @@ upgrade:
|
|||||||
* Infortrend
|
* Infortrend
|
||||||
* QNAP
|
* QNAP
|
||||||
* Reduxio
|
* Reduxio
|
||||||
|
* Violin
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user