diff --git a/cinder/exception.py b/cinder/exception.py index c927dc5037b..ab4c590fff0 100644 --- a/cinder/exception.py +++ b/cinder/exception.py @@ -1198,31 +1198,6 @@ class XIODriverException(VolumeDriverException): 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): message = _("Bad HTTP response status %(status)s") diff --git a/cinder/opts.py b/cinder/opts.py index 5cf1aa142c5..9ba51d498fb 100644 --- a/cinder/opts.py +++ b/cinder/opts.py @@ -169,8 +169,6 @@ from cinder.volume.drivers.synology import synology_common as \ cinder_volume_drivers_synology_synologycommon 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.violin import v7000_common as \ - cinder_volume_drivers_violin_v7000common from cinder.volume.drivers.vmware import vmdk as \ cinder_volume_drivers_vmware_vmdk 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_tegile.tegile_opts, cinder_volume_drivers_tintri.tintri_opts, - cinder_volume_drivers_violin_v7000common.violin_opts, cinder_volume_drivers_vmware_vmdk.vmdk_opts, cinder_volume_drivers_vzstorage.vzstorage_opts, cinder_volume_drivers_windows_smbfs.volume_opts, diff --git a/cinder/tests/unit/volume/drivers/test_v7000_iscsi.py b/cinder/tests/unit/volume/drivers/test_v7000_iscsi.py deleted file mode 100644 index e01bf29cc32..00000000000 --- a/cinder/tests/unit/volume/drivers/test_v7000_iscsi.py +++ /dev/null @@ -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) diff --git a/cinder/tests/unit/volume/drivers/violin/__init__.py b/cinder/tests/unit/volume/drivers/violin/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/cinder/tests/unit/volume/drivers/violin/fake_vmem_client.py b/cinder/tests/unit/volume/drivers/violin/fake_vmem_client.py deleted file mode 100644 index db42bfc05a7..00000000000 --- a/cinder/tests/unit/volume/drivers/violin/fake_vmem_client.py +++ /dev/null @@ -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', -] diff --git a/cinder/tests/unit/volume/drivers/violin/test_v7000_common.py b/cinder/tests/unit/volume/drivers/violin/test_v7000_common.py deleted file mode 100644 index 8f7110753b8..00000000000 --- a/cinder/tests/unit/volume/drivers/violin/test_v7000_common.py +++ /dev/null @@ -1,1417 +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 Common Driver -""" -import ddt -import math -import mock -import six - -from oslo_utils import units - -from cinder import context -from cinder import exception -from cinder import test -from cinder.tests.unit import fake_constants as fake -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 import volume_types - - -VOLUME_ID = "abcdabcd-1234-abcd-1234-abcdeffedcba" -VOLUME = {"name": "volume-" + VOLUME_ID, - "id": VOLUME_ID, - "display_name": "fake_volume", - "size": 2, - "host": "irrelevant", - "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", - } -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": "irrelevant", - "volume_type": None, - "volume_type_id": None, - } -INITIATOR_IQN = "iqn.1111-22.org.debian:11:222" -CONNECTOR = {"initiator": INITIATOR_IQN} -DEFAULT_DEDUP_POOL = {"storage_pool": 'PoolA', - "storage_pool_id": 99, - "dedup": True, - "thin": True, - } -DEFAULT_THIN_POOL = {"storage_pool": 'PoolA', - "storage_pool_id": 99, - "dedup": False, - "thin": True, - } -DEFAULT_THICK_POOL = {"storage_pool": 'PoolA', - "storage_pool_id": 99, - "dedup": False, - "thin": False, - } - -# Note: select superfluous fields are removed for brevity -STATS_STORAGE_POOL_RESPONSE = [({ - 'availsize_mb': 1572827, - 'category': 'Virtual Device', - 'name': 'dedup-pool', - 'object_id': '487d1940-c53f-55c3-b1d5-073af43f80fc', - 'size_mb': 2097124, - 'storage_pool_id': 1, - 'usedsize_mb': 524297}, - {'category': 'Virtual Device', - 'name': 'dedup-pool', - 'object_id': '487d1940-c53f-55c3-b1d5-073af43f80fc', - 'physicaldevices': [ - {'availsize_mb': 524281, - 'connection_type': 'fc', - 'name': 'VIOLIN:CONCERTO ARRAY.003', - 'object_id': '260f30b0-0300-59b5-b7b9-54aa55704a12', - 'owner': 'lab-host1', - 'size_mb': 524281, - 'type': 'Direct-Access', - 'usedsize_mb': 0}, - {'availsize_mb': 524281, - 'connection_type': 'fc', - 'name': 'VIOLIN:CONCERTO ARRAY.004', - 'object_id': '7b58eda2-69da-5aec-9e06-6607934efa93', - 'owner': 'lab-host1', - 'size_mb': 524281, - 'type': 'Direct-Access', - 'usedsize_mb': 0}, - {'availsize_mb': 0, - 'connection_type': 'fc', - 'name': 'VIOLIN:CONCERTO ARRAY.001', - 'object_id': '69adbea1-2349-5df5-a04a-abd7f14868b2', - 'owner': 'lab-host1', - 'size_mb': 524281, - 'type': 'Direct-Access', - 'usedsize_mb': 524281}, - {'availsize_mb': 524265, - 'connection_type': 'fc', - 'name': 'VIOLIN:CONCERTO ARRAY.002', - 'object_id': 'a14a0e36-8901-5987-95d8-aa574c6138a2', - 'owner': 'lab-host1', - 'size_mb': 524281, - 'type': 'Direct-Access', - 'usedsize_mb': 16}], - 'size_mb': 2097124, - 'storage_pool_id': 1, - 'total_physicaldevices': 4, - 'usedsize_mb': 524297}), - ({'availsize': 0, - 'availsize_mb': 0, - 'category': None, - 'name': 'thick_pool_13531mgb', - 'object_id': '20610abd-4c58-546c-8905-bf42fab9a11b', - 'size': 0, - 'size_mb': 0, - 'storage_pool_id': 3, - 'tag': '', - 'total_physicaldevices': 0, - 'usedsize': 0, - 'usedsize_mb': 0}, - {'category': None, - 'name': 'thick_pool_13531mgb', - 'object_id': '20610abd-4c58-546c-8905-bf42fab9a11b', - 'resource_type': ['All'], - 'size': 0, - 'size_mb': 0, - 'storage_pool_id': 3, - 'tag': [''], - 'total_physicaldevices': 0, - 'usedsize': 0, - 'usedsize_mb': 0}), - ({'availsize_mb': 627466, - 'category': 'Virtual Device', - 'name': 'StoragePool', - 'object_id': '1af66d9a-f62e-5b69-807b-892b087fa0b4', - 'size_mb': 21139267, - 'storage_pool_id': 7, - 'usedsize_mb': 20511801}, - {'category': 'Virtual Device', - 'name': 'StoragePool', - 'object_id': '1af66d9a-f62e-5b69-807b-892b087fa0b4', - 'physicaldevices': [ - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN02.000', - 'object_id': 'ecc775f1-1228-5131-8f68-4176001786ef', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN01.000', - 'object_id': '5c60812b-34d2-5473-b7bf-21e30ec70311', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN08.001', - 'object_id': 'eb6d06b7-8d6f-5d9d-b720-e86d8ad1beab', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN03.001', - 'object_id': '063aced7-1f8f-5e15-b36e-e9d34a2826fa', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN07.001', - 'object_id': 'ebf34594-2b92-51fe-a6a8-b6cf91f05b2b', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN0A.000', - 'object_id': 'ff084188-b97f-5e30-9ff0-bc60e546ee06', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN06.001', - 'object_id': 'f9cbeadf-5524-5697-a3a6-667820e37639', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 167887, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN15.000', - 'object_id': 'aaacc124-26c9-519a-909a-a93d24f579a1', - 'owner': 'lab-host2', - 'size_mb': 167887, - 'type': 'Direct-Access', - 'usedsize_mb': 0}, - {'availsize_mb': 229276, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN09.001', - 'object_id': '30967a84-56a4-52a5-ac3f-b4f544257bbd', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 819293}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN04.001', - 'object_id': 'd997eb42-55d4-5e4c-b797-c68b748e7e1f', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN05.001', - 'object_id': '56ecf98c-f10b-5bb5-9d3b-5af6037dad73', - 'owner': 'lab-host1', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN0B.000', - 'object_id': 'cfb6f61c-508d-5394-8257-78b1f9bcad3b', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN0C.000', - 'object_id': '7b0bcb51-5c7d-5752-9e18-392057e534f0', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN0D.000', - 'object_id': 'b785a3b1-6316-50c3-b2e0-6bb0739499c6', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN0E.000', - 'object_id': '76b9d038-b757-515a-b962-439a4fd85fd5', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN0F.000', - 'object_id': '9591d24a-70c4-5e80-aead-4b788202c698', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN10.000', - 'object_id': '2bb09a2b-9063-595b-9d7a-7e5fad5016db', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN11.000', - 'object_id': 'b9ff58eb-5e6e-5c79-bf95-fae424492519', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN12.000', - 'object_id': '6abd4fd6-9841-5978-bfcb-5d398d1715b4', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}, - {'availsize_mb': 230303, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN13.000', - 'object_id': 'ffd5a4b7-0f50-5a71-bbba-57a348b96c68', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 818266}, - {'availsize_mb': 0, - 'connection_type': 'block', - 'name': 'BKSC:OTHDISK-MFCN14.000', - 'object_id': '52ffbbae-bdac-5194-ba6b-62ee17bfafce', - 'owner': 'lab-host2', - 'size_mb': 1048569, - 'type': 'Direct-Access', - 'usedsize_mb': 1048569}], - 'size_mb': 21139267, - 'storage_pool_id': 7, - 'tag': [''], - 'total_physicaldevices': 21, - 'usedsize_mb': 20511801}), - ({'availsize_mb': 1048536, - 'category': 'Virtual Device', - 'name': 'thick-pool', - 'object_id': 'c1e0becc-3497-5d74-977a-1e5a79769576', - 'size_mb': 2097124, - 'storage_pool_id': 9, - 'usedsize_mb': 1048588}, - {'category': 'Virtual Device', - 'name': 'thick-pool', - 'object_id': 'c1e0becc-3497-5d74-977a-1e5a79769576', - 'physicaldevices': [ - {'availsize_mb': 524255, - 'connection_type': 'fc', - 'name': 'VIOLIN:CONCERTO ARRAY.001', - 'object_id': 'a90c4a11-33af-5530-80ca-2360fa477781', - 'owner': 'lab-host1', - 'size_mb': 524281, - 'type': 'Direct-Access', - 'usedsize_mb': 26}, - {'availsize_mb': 0, - 'connection_type': 'fc', - 'name': 'VIOLIN:CONCERTO ARRAY.002', - 'object_id': '0a625ec8-2e80-5086-9644-2ea8dd5c32ec', - 'owner': 'lab-host1', - 'size_mb': 524281, - 'type': 'Direct-Access', - 'usedsize_mb': 524281}, - {'availsize_mb': 0, - 'connection_type': 'fc', - 'name': 'VIOLIN:CONCERTO ARRAY.004', - 'object_id': '7018670b-3a79-5bdc-9d02-2d85602f361a', - 'owner': 'lab-host1', - 'size_mb': 524281, - 'type': 'Direct-Access', - 'usedsize_mb': 524281}, - {'availsize_mb': 524281, - 'connection_type': 'fc', - 'name': 'VIOLIN:CONCERTO ARRAY.003', - 'object_id': 'd859d47b-ca65-5d9d-a1c0-e288bbf39f48', - 'owner': 'lab-host1', - 'size_mb': 524281, - 'type': 'Direct-Access', - 'usedsize_mb': 0}], - 'size_mb': 2097124, - 'storage_pool_id': 9, - 'total_physicaldevices': 4, - 'usedsize_mb': 1048588})] - - -@ddt.ddt -class V7000CommonTestCase(test.TestCase): - """Test case for Violin drivers.""" - def setUp(self): - super(V7000CommonTestCase, self).setUp() - self.conf = self.setup_configuration() - self.driver = v7000_common.V7000Common(self.conf) - self.driver.container = 'myContainer' - self.driver.device_id = 'ata-VIOLIN_MEMORY_ARRAY_23109R00000022' - self.stats = {} - - def setup_configuration(self): - config = mock.Mock(spec=conf.Configuration) - config.volume_backend_name = 'v7000_common' - config.san_ip = '1.1.1.1' - config.san_login = 'admin' - config.san_password = '' - config.san_thin_provision = False - config.san_is_local = False - config.gateway_mga = '2.2.2.2' - config.gateway_mgb = '3.3.3.3' - config.use_igroups = False - config.violin_request_timeout = 300 - config.container = 'myContainer' - config.violin_pool_allocation_method = 'random' - config.violin_dedup_only_pools = None - config.violin_dedup_capable_pools = None - return config - - @mock.patch('vmemclient.open') - def setup_mock_client(self, _m_client, m_conf=None): - """Create a fake backend communication factory. - - The xg-tools creates a Concerto connection object (for V7000 - devices) and returns it for use on a call to vmemclient.open(). - """ - # configure the concerto object mock with defaults - _m_concerto = mock.Mock(name='Concerto', - version='1.1.1', - spec=vmemclient.mock_client_conf) - - # if m_conf, clobber the defaults with it - if m_conf: - _m_concerto.configure_mock(**m_conf) - - # set calls to vmemclient.open() to return this mocked concerto object - _m_client.return_value = _m_concerto - - return _m_client - - 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 - - def test_check_for_setup_error(self): - """No setup errors are found.""" - self.driver.vmem_mg = self.setup_mock_concerto() - self.driver._is_supported_vmos_version = mock.Mock(return_value=True) - - result = self.driver.check_for_setup_error() - - self.driver._is_supported_vmos_version.assert_called_with( - self.driver.vmem_mg.version) - self.assertIsNone(result) - - def test_create_lun(self): - """Lun is successfully created.""" - response = {'success': True, 'msg': 'Create resource successfully.'} - size_in_mb = VOLUME['size'] * units.Ki - - conf = { - 'lun.create_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._send_cmd = mock.Mock(return_value=response) - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - - result = self.driver._create_lun(VOLUME) - - self.driver._send_cmd.assert_called_with( - self.driver.vmem_mg.lun.create_lun, - 'Create resource successfully.', - VOLUME['id'], size_in_mb, False, False, size_in_mb, - storage_pool_id=99) - self.assertIsNone(result) - - def test_create_dedup_lun(self): - """Lun is successfully created.""" - vol = VOLUME.copy() - vol['size'] = 100 - vol['volume_type_id'] = '1' - - response = {'success': True, 'msg': 'Create resource successfully.'} - size_in_mb = vol['size'] * units.Ki - full_size_mb = size_in_mb - - conf = { - 'lun.create_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._send_cmd = mock.Mock(return_value=response) - - # simulate extra specs of {'thin': 'true', 'dedupe': 'true'} - self.driver._get_volume_type_extra_spec = mock.Mock( - return_value="True") - - self.driver._get_violin_extra_spec = mock.Mock( - return_value=None) - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_DEDUP_POOL) - - result = self.driver._create_lun(vol) - - self.driver._send_cmd.assert_called_with( - self.driver.vmem_mg.lun.create_lun, - 'Create resource successfully.', - VOLUME['id'], size_in_mb / 10, True, True, full_size_mb, - storage_pool_id=99) - self.assertIsNone(result) - - def test_fail_extend_dedup_lun(self): - """Volume extend fails when new size would shrink the volume.""" - vol = VOLUME.copy() - vol['volume_type_id'] = '1' - - size_in_mb = vol['size'] * units.Ki - self.driver.vmem_mg = self.setup_mock_concerto() - type(self.driver.vmem_mg.utility).is_external_head = mock.PropertyMock( - return_value=False) - - self.driver._get_volume_type_extra_spec = mock.Mock( - return_value="True") - - failure = exception.VolumeDriverException - self.assertRaises(failure, self.driver._extend_lun, - vol, size_in_mb) - - def test_extend_dedup_lun_external_head(self): - """Volume extend fails when new size would shrink the volume.""" - vol = VOLUME.copy() - vol['volume_type_id'] = '1' - new_volume_size = 10 - - response = {'success': True, 'message': 'Expand resource successfully'} - conf = { - 'lun.extend_lun.return_value': response, - } - - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - type(self.driver.vmem_mg.utility).is_external_head = mock.PropertyMock( - return_value=False) - - change_in_size_mb = (new_volume_size - VOLUME['size']) * units.Ki - self.driver._send_cmd = mock.Mock(return_value=response) - - result = self.driver._extend_lun(VOLUME, new_volume_size) - - self.driver._send_cmd.assert_called_with( - self.driver.vmem_mg.lun.extend_lun, - response['message'], VOLUME['id'], change_in_size_mb) - self.assertIsNone(result) - - def test_create_non_dedup_lun(self): - """Lun is successfully created.""" - vol = VOLUME.copy() - vol['size'] = 100 - vol['volume_type_id'] = '1' - - response = {'success': True, 'msg': 'Create resource successfully.'} - size_in_mb = vol['size'] * units.Ki - full_size_mb = size_in_mb - - conf = { - 'lun.create_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._send_cmd = mock.Mock(return_value=response) - - # simulate extra specs of {'thin': 'false', 'dedupe': 'false'} - self.driver._get_volume_type_extra_spec = mock.Mock( - return_value="False") - - self.driver._get_violin_extra_spec = mock.Mock( - return_value=None) - - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - - result = self.driver._create_lun(vol) - - self.driver._send_cmd.assert_called_with( - self.driver.vmem_mg.lun.create_lun, - 'Create resource successfully.', - VOLUME['id'], size_in_mb, False, False, full_size_mb, - storage_pool_id=99) - self.assertIsNone(result) - - def test_create_lun_fails(self): - """Array returns error that the lun already exists.""" - response = {'success': False, - 'msg': 'Duplicate Virtual Device name. Error: 0x90010022'} - conf = { - 'lun.create_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - self.driver._send_cmd = mock.Mock(return_value=response) - - self.assertIsNone(self.driver._create_lun(VOLUME)) - - def test_create_lun_on_a_storage_pool(self): - """Lun is successfully created.""" - vol = VOLUME.copy() - vol['size'] = 100 - vol['volume_type_id'] = '1' - - response = {'success': True, 'msg': 'Create resource successfully.'} - size_in_mb = vol['size'] * units.Ki - full_size_mb = size_in_mb - - conf = { - 'lun.create_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._send_cmd = mock.Mock(return_value=response) - self.driver._get_volume_type_extra_spec = mock.Mock( - return_value="False") - - # simulates extra specs: {'storage_pool', 'StoragePool'} - self.driver._get_violin_extra_spec = mock.Mock( - return_value="StoragePool") - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - - result = self.driver._create_lun(vol) - - self.driver._send_cmd.assert_called_with( - self.driver.vmem_mg.lun.create_lun, - 'Create resource successfully.', - VOLUME['id'], size_in_mb, False, False, full_size_mb, - storage_pool_id=99) - self.assertIsNone(result) - - def test_delete_lun(self): - """Lun is deleted successfully.""" - response = {'success': True, 'msg': 'Delete resource successfully'} - success_msgs = ['Delete resource successfully', ''] - - conf = { - 'lun.delete_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._send_cmd = mock.Mock(return_value=response) - self.driver._delete_lun_snapshot_bookkeeping = mock.Mock() - - result = self.driver._delete_lun(VOLUME) - - self.driver._send_cmd.assert_called_with( - self.driver.vmem_mg.lun.delete_lun, - success_msgs, VOLUME['id']) - self.driver._delete_lun_snapshot_bookkeeping.assert_called_with( - VOLUME['id']) - - self.assertIsNone(result) - - # TODO(vthirumalai): More delete lun failure cases to be added after - # collecting the possible responses from Concerto - - def test_extend_lun(self): - """Volume extend completes successfully.""" - new_volume_size = 10 - change_in_size_mb = (new_volume_size - VOLUME['size']) * units.Ki - - response = {'success': True, 'message': 'Expand resource successfully'} - - conf = { - 'lun.extend_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._send_cmd = mock.Mock(return_value=response) - - result = self.driver._extend_lun(VOLUME, new_volume_size) - - self.driver._send_cmd.assert_called_with( - self.driver.vmem_mg.lun.extend_lun, - response['message'], VOLUME['id'], change_in_size_mb) - self.assertIsNone(result) - - def test_extend_lun_new_size_is_too_small(self): - """Volume extend fails when new size would shrink the volume.""" - new_volume_size = 0 - change_in_size_mb = (new_volume_size - VOLUME['size']) * units.Ki - - response = {'success': False, 'msg': 'Invalid size. Error: 0x0902000c'} - failure = exception.ViolinBackendErr - - conf = { - 'lun.resize_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._send_cmd = mock.Mock(side_effect=failure(message='fail')) - - self.assertRaises(failure, self.driver._extend_lun, - VOLUME, change_in_size_mb) - - def test_create_volume_from_snapshot(self): - """Create a new cinder volume from a given snapshot of a lun.""" - object_id = '12345' - vdev_id = 11111 - lun_info_response = {'subType': 'THICK', - 'virtualDeviceID': vdev_id} - response = {'success': True, - 'object_id': object_id, - 'msg': 'Copy TimeMark successfully.'} - compressed_snap_id = 'abcdabcd1234abcd1234abcdeffedcbb' - - conf = { - 'lun.get_lun_info.return_value': lun_info_response, - 'lun.copy_snapshot_to_new_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._compress_snapshot_id = mock.Mock( - return_value=compressed_snap_id) - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - self.driver._wait_for_lun_or_snap_copy = mock.Mock() - - result = self.driver._create_volume_from_snapshot(SNAPSHOT, VOLUME) - - self.driver.vmem_mg.lun.copy_snapshot_to_new_lun.assert_called_with( - source_lun=SNAPSHOT['volume_id'], - source_snapshot_comment=compressed_snap_id, - destination=VOLUME['id'], storage_pool_id=99) - self.driver._wait_for_lun_or_snap_copy.assert_called_with( - SNAPSHOT['volume_id'], dest_vdev_id=vdev_id) - - self.assertIsNone(result) - - def test_create_volume_from_snapshot_on_a_storage_pool(self): - """Create a new cinder volume from a given snapshot of a lun.""" - dest_vol = VOLUME.copy() - dest_vol['size'] = 100 - dest_vol['volume_type_id'] = '1' - object_id = '12345' - vdev_id = 11111 - lun_info_response = {'subType': 'THICK', - 'virtualDeviceID': vdev_id} - response = {'success': True, - 'object_id': object_id, - 'msg': 'Copy TimeMark successfully.'} - compressed_snap_id = 'abcdabcd1234abcd1234abcdeffedcbb' - - conf = { - 'lun.get_lun_info.return_value': lun_info_response, - 'lun.copy_snapshot_to_new_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._compress_snapshot_id = mock.Mock( - return_value=compressed_snap_id) - self.driver._get_violin_extra_spec = mock.Mock( - return_value="StoragePool") - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - self.driver._get_volume_type_extra_spec = mock.Mock( - return_value="False") - self.driver._wait_for_lun_or_snap_copy = mock.Mock() - - result = self.driver._create_volume_from_snapshot(SNAPSHOT, dest_vol) - - self.assertIsNone(result) - - def test_create_volume_from_snapshot_fails(self): - """Array returns error that the lun already exists.""" - vdev_id = 11111 - lun_info_response = {'subType': 'THICK', - 'virtualDeviceID': vdev_id} - response = {'success': False, - 'msg': 'Duplicate Virtual Device name. Error: 0x90010022'} - compressed_snap_id = 'abcdabcd1234abcd1234abcdeffedcbb' - failure = exception.ViolinBackendErrExists - - conf = { - 'lun.get_lun_info.return_value': lun_info_response, - 'lun.copy_snapshot_to_new_lun.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._send_cmd = mock.Mock(return_value=response) - self.driver._compress_snapshot_id = mock.Mock( - return_value=compressed_snap_id) - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - - self.driver._send_cmd = mock.Mock(side_effect=failure(message='fail')) - - self.assertRaises(failure, self.driver._create_volume_from_snapshot, - SNAPSHOT, VOLUME) - - @ddt.data(2, 10) - def test_create_lun_from_lun_and_resize(self, size): - """lun full clone to new volume completes successfully.""" - larger_size_flag = False - dest_vol = VOLUME.copy() - if size > VOLUME['size']: - dest_vol['size'] = size - larger_size_flag = True - object_id = fake.OBJECT_ID - lun_info_response = {'subType': 'THICK'} - copy_response = {'success': True, - 'object_id': object_id, - 'msg': 'Copy Snapshot resource successfully'} - - conf = { - 'lun.get_lun_info.return_value': lun_info_response, - 'lun.copy_lun_to_new_lun.return_value': copy_response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._ensure_snapshot_resource_area = mock.Mock() - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - self.driver._wait_for_lun_or_snap_copy = mock.Mock() - self.driver._extend_lun = mock.Mock() - - result = self.driver._create_lun_from_lun(SRC_VOL, dest_vol) - - self.driver._ensure_snapshot_resource_area.assert_called_with( - SRC_VOL['id']) - self.driver.vmem_mg.lun.copy_lun_to_new_lun.assert_called_with( - source=SRC_VOL['id'], destination=VOLUME['id'], storage_pool_id=99) - self.driver._wait_for_lun_or_snap_copy.assert_called_with( - SRC_VOL['id'], dest_obj_id=object_id) - if larger_size_flag: - self.driver._extend_lun.assert_called_once_with( - dest_vol, dest_vol['size']) - else: - self.assertFalse(self.driver._extend_lun.called) - - self.assertIsNone(result) - - @ddt.data(2, 10) - def test_create_lun_from_lun_on_a_storage_pool_and_resize(self, size): - """lun full clone to new volume completes successfully.""" - larger_size_flag = False - dest_vol = VOLUME.copy() - if size > VOLUME['size']: - dest_vol['size'] = size - larger_size_flag = True - dest_vol['volume_type_id'] = fake.VOLUME_TYPE_ID - object_id = fake.OBJECT_ID - lun_info_response = {'subType': 'THICK'} - copy_response = {'success': True, - 'object_id': object_id, - 'msg': 'Copy Snapshot resource successfully'} - - conf = { - 'lun.get_lun_info.return_value': lun_info_response, - 'lun.copy_lun_to_new_lun.return_value': copy_response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._ensure_snapshot_resource_area = mock.Mock() - self.driver._wait_for_lun_or_snap_copy = mock.Mock() - self.driver._extend_lun = mock.Mock() - - # simulates extra specs: {'storage_pool', 'StoragePool'} - self.driver._get_violin_extra_spec = mock.Mock( - return_value="StoragePool") - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THIN_POOL) - - self.driver._get_volume_type_extra_spec = mock.Mock( - side_effect=["True", "False"]) - - result = self.driver._create_lun_from_lun(SRC_VOL, dest_vol) - - self.driver._ensure_snapshot_resource_area.assert_called_with( - SRC_VOL['id']) - self.driver.vmem_mg.lun.copy_lun_to_new_lun.assert_called_with( - source=SRC_VOL['id'], destination=dest_vol['id'], - storage_pool_id=99) - self.driver._wait_for_lun_or_snap_copy.assert_called_with( - SRC_VOL['id'], dest_obj_id=object_id) - if larger_size_flag: - self.driver._extend_lun.assert_called_once_with( - dest_vol, dest_vol['size']) - else: - self.assertFalse(self.driver._extend_lun.called) - - self.assertIsNone(result) - - def test_create_lun_from_lun_fails(self): - """lun full clone to new volume fails correctly.""" - failure = exception.ViolinBackendErr - lun_info_response = { - 'subType': 'THICK', - } - copy_response = { - 'success': False, - 'msg': 'Snapshot Resource is not created ' + - 'for this virtual device. Error: 0x0901008c', - } - - conf = { - 'lun.get_lun_info.return_value': lun_info_response, - 'lun.copy_lun_to_new_lun.return_value': copy_response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._ensure_snapshot_resource_area = mock.Mock() - self.driver._send_cmd = mock.Mock(side_effect=failure(message='fail')) - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - - self.assertRaises(failure, self.driver._create_lun_from_lun, - SRC_VOL, VOLUME) - - def test_create_lun_from_thin_lun_fails(self): - """lun full clone of thin lun is not supported.""" - failure = exception.ViolinBackendErr - lun_info_response = { - 'subType': 'THIN', - } - - conf = { - 'lun.get_lun_info.return_value': lun_info_response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - - self.assertRaises(failure, self.driver._create_lun_from_lun, - SRC_VOL, VOLUME) - - def test_create_lun_from_dedup_lun_fails(self): - """lun full clone of dedup lun is not supported.""" - failure = exception.ViolinBackendErr - lun_info_response = { - 'subType': 'DEDUP', - } - - conf = { - 'lun.get_lun_info.return_value': lun_info_response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - - self.assertRaises(failure, self.driver._create_lun_from_lun, - SRC_VOL, VOLUME) - - def test_send_cmd(self): - """Command callback completes successfully.""" - success_msg = 'success' - request_args = ['arg1', 'arg2', 'arg3'] - response = {'success': True, 'msg': 'Operation successful'} - - request_func = mock.Mock(return_value=response) - - result = self.driver._send_cmd(request_func, success_msg, request_args) - - self.assertEqual(response, result) - - def test_send_cmd_request_timed_out(self): - """The callback retry timeout hits immediately.""" - failure = exception.ViolinRequestRetryTimeout - success_msg = 'success' - request_args = ['arg1', 'arg2', 'arg3'] - self.conf.violin_request_timeout = 0 - - request_func = mock.Mock() - - self.assertRaises(failure, self.driver._send_cmd, - request_func, success_msg, request_args) - - def test_send_cmd_response_has_no_message(self): - """The callback returns no message on the first call.""" - success_msg = 'success' - request_args = ['arg1', 'arg2', 'arg3'] - response1 = {'success': True, 'msg': None} - response2 = {'success': True, 'msg': 'success'} - - request_func = mock.Mock(side_effect=[response1, response2]) - - self.assertEqual(response2, self.driver._send_cmd - (request_func, success_msg, request_args)) - - def test_check_error_code(self): - """Return an exception for a valid error code.""" - failure = exception.ViolinBackendErr - response = {'success': False, 'msg': 'Error: 0x90000000'} - self.assertRaises(failure, self.driver._check_error_code, - response) - - def test_check_error_code_non_fatal_error(self): - """Returns no exception for a non-fatal error code.""" - response = {'success': False, 'msg': 'Error: 0x9001003c'} - self.assertIsNone(self.driver._check_error_code(response)) - - def test_compress_snapshot_id(self): - test_snap_id = "12345678-abcd-1234-cdef-0123456789ab" - expected = "12345678abcd1234cdef0123456789ab" - - self.assertEqual(32, len(expected)) - result = self.driver._compress_snapshot_id(test_snap_id) - self.assertEqual(expected, result) - - def test_ensure_snapshot_resource_area(self): - result_dict = {'success': True, 'res': 'Successful'} - - self.driver.vmem_mg = self.setup_mock_concerto() - snap = self.driver.vmem_mg.snapshot - snap.lun_has_a_snapshot_resource = mock.Mock(return_value=False) - snap.create_snapshot_resource = mock.Mock(return_value=result_dict) - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - - with mock.patch('cinder.db.sqlalchemy.api.volume_get', - return_value=VOLUME): - result = self.driver._ensure_snapshot_resource_area(VOLUME_ID) - - self.assertIsNone(result) - snap.lun_has_a_snapshot_resource.assert_called_with(lun=VOLUME_ID) - snap.create_snapshot_resource.assert_called_with( - lun=VOLUME_ID, - size=int(math.ceil(0.2 * (VOLUME['size'] * 1024))), - enable_notification=False, - policy=v7000_common.CONCERTO_DEFAULT_SRA_POLICY, - enable_expansion= - v7000_common.CONCERTO_DEFAULT_SRA_ENABLE_EXPANSION, - expansion_threshold= - v7000_common.CONCERTO_DEFAULT_SRA_EXPANSION_THRESHOLD, - expansion_increment= - v7000_common.CONCERTO_DEFAULT_SRA_EXPANSION_INCREMENT, - expansion_max_size= - v7000_common.CONCERTO_DEFAULT_SRA_EXPANSION_MAX_SIZE, - enable_shrink=v7000_common.CONCERTO_DEFAULT_SRA_ENABLE_SHRINK, - storage_pool_id=99) - - def test_ensure_snapshot_resource_area_with_storage_pool(self): - - dest_vol = VOLUME.copy() - dest_vol['size'] = 2 - dest_vol['volume_type_id'] = '1' - - result_dict = {'success': True, 'res': 'Successful'} - - self.driver.vmem_mg = self.setup_mock_concerto() - snap = self.driver.vmem_mg.snapshot - snap.lun_has_a_snapshot_resource = mock.Mock(return_value=False) - snap.create_snapshot_resource = mock.Mock(return_value=result_dict) - - # simulates extra specs: {'storage_pool', 'StoragePool'} - self.driver._get_violin_extra_spec = mock.Mock( - return_value="StoragePool") - self.driver._get_storage_pool = mock.Mock( - return_value=DEFAULT_THICK_POOL) - - self.driver._get_volume_type_extra_spec = mock.Mock( - side_effect=["True", "False"]) - - with mock.patch('cinder.db.sqlalchemy.api.volume_get', - return_value=dest_vol): - result = self.driver._ensure_snapshot_resource_area(VOLUME_ID) - - self.assertIsNone(result) - snap.lun_has_a_snapshot_resource.assert_called_with(lun=VOLUME_ID) - snap.create_snapshot_resource.assert_called_with( - lun=VOLUME_ID, - size=int(math.ceil(0.2 * (VOLUME['size'] * 1024))), - enable_notification=False, - policy=v7000_common.CONCERTO_DEFAULT_SRA_POLICY, - enable_expansion= - v7000_common.CONCERTO_DEFAULT_SRA_ENABLE_EXPANSION, - expansion_threshold= - v7000_common.CONCERTO_DEFAULT_SRA_EXPANSION_THRESHOLD, - expansion_increment= - v7000_common.CONCERTO_DEFAULT_SRA_EXPANSION_INCREMENT, - expansion_max_size= - v7000_common.CONCERTO_DEFAULT_SRA_EXPANSION_MAX_SIZE, - enable_shrink=v7000_common.CONCERTO_DEFAULT_SRA_ENABLE_SHRINK, - storage_pool_id=99) - - def test_ensure_snapshot_resource_policy(self): - result_dict = {'success': True, 'res': 'Successful'} - - self.driver.vmem_mg = self.setup_mock_concerto() - - snap = self.driver.vmem_mg.snapshot - snap.lun_has_a_snapshot_policy = mock.Mock(return_value=False) - snap.create_snapshot_policy = mock.Mock(return_value=result_dict) - - result = self.driver._ensure_snapshot_policy(VOLUME_ID) - self.assertIsNone(result) - snap.lun_has_a_snapshot_policy.assert_called_with(lun=VOLUME_ID) - - snap.create_snapshot_policy.assert_called_with( - lun=VOLUME_ID, - max_snapshots=v7000_common.CONCERTO_DEFAULT_POLICY_MAX_SNAPSHOTS, - enable_replication=False, - enable_snapshot_schedule=False, - enable_cdp=False, - retention_mode=v7000_common.CONCERTO_DEFAULT_POLICY_RETENTION_MODE) - - def test_delete_lun_snapshot_bookkeeping(self): - result_dict = {'success': True, 'res': 'Successful'} - - self.driver.vmem_mg = self.setup_mock_concerto() - snap = self.driver.vmem_mg.snapshot - snap.get_snapshots = mock.Mock( - return_value=[], - side_effect=vmemclient.core.error.NoMatchingObjectIdError) - snap.delete_snapshot_policy = mock.Mock(return_value=result_dict) - snap.delete_snapshot_resource = mock.Mock() - - result = self.driver._delete_lun_snapshot_bookkeeping( - volume_id=VOLUME_ID) - - self.assertIsNone(result) - - snap.get_snapshots.assert_called_with(VOLUME_ID) - snap.delete_snapshot_policy.assert_called_with(lun=VOLUME_ID) - snap.delete_snapshot_resource.assert_called_with(lun=VOLUME_ID) - - def test_create_lun_snapshot(self): - response = {'success': True, 'msg': 'Create TimeMark successfully'} - - self.driver.vmem_mg = self.setup_mock_concerto() - self.driver._ensure_snapshot_resource_area = ( - mock.Mock(return_value=True)) - self.driver._ensure_snapshot_policy = mock.Mock(return_value=True) - self.driver._send_cmd = mock.Mock(return_value=response) - - with mock.patch('cinder.db.sqlalchemy.api.volume_get', - return_value=VOLUME): - result = self.driver._create_lun_snapshot(SNAPSHOT) - - self.assertIsNone(result) - - self.driver._ensure_snapshot_resource_area.assert_called_with( - VOLUME_ID) - self.driver._ensure_snapshot_policy.assert_called_with(VOLUME_ID) - self.driver._send_cmd.assert_called_with( - self.driver.vmem_mg.snapshot.create_lun_snapshot, - 'Create TimeMark successfully', - lun=VOLUME_ID, - comment=self.driver._compress_snapshot_id(SNAPSHOT_ID), - priority=v7000_common.CONCERTO_DEFAULT_PRIORITY, - enable_notification=False) - - def test_delete_lun_snapshot(self): - response = {'success': True, 'msg': 'Delete TimeMark successfully'} - compressed_snap_id = 'abcdabcd1234abcd1234abcdeffedcbb' - oid = 'abc123-abc123abc123-abc123' - - conf = { - 'snapshot.snapshot_comment_to_object_id.return_value': oid, - 'snapshot.delete_lun_snapshot.return_value': response, - } - - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._compress_snapshot_id = mock.Mock( - return_value=compressed_snap_id) - - result = self.driver._delete_lun_snapshot(SNAPSHOT) - - self.assertTrue(result) - - def test_delete_lun_snapshot_with_retry(self): - response = [ - {'success': False, 'msg': 'Error 0x50f7564c'}, - {'success': True, 'msg': 'Delete TimeMark successfully'}] - compressed_snap_id = 'abcdabcd1234abcd1234abcdeffedcbb' - oid = 'abc123-abc123abc123-abc123' - - conf = { - 'snapshot.snapshot_comment_to_object_id.return_value': oid, - 'snapshot.delete_lun_snapshot.side_effect': response, - } - - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._compress_snapshot_id = mock.Mock( - return_value=compressed_snap_id) - - result = self.driver._delete_lun_snapshot(SNAPSHOT) - - self.assertTrue(result) - self.assertEqual( - len(response), - self.driver.vmem_mg.snapshot.delete_lun_snapshot.call_count) - - def test_wait_for_lun_or_snap_copy_completes_for_snap(self): - """waiting for a snapshot to copy succeeds.""" - vdev_id = 11111 - response = (vdev_id, None, 100) - - conf = { - 'snapshot.get_snapshot_copy_status.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - - result = self.driver._wait_for_lun_or_snap_copy( - SRC_VOL['id'], dest_vdev_id=vdev_id) - - (self.driver.vmem_mg.snapshot.get_snapshot_copy_status. - assert_called_with(SRC_VOL['id'])) - self.assertTrue(result) - - def test_wait_for_lun_or_snap_copy_completes_for_lun(self): - """waiting for a lun to copy succeeds.""" - object_id = '12345' - response = (object_id, None, 100) - - conf = { - 'lun.get_lun_copy_status.return_value': response, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - - result = self.driver._wait_for_lun_or_snap_copy( - SRC_VOL['id'], dest_obj_id=object_id) - - self.driver.vmem_mg.lun.get_lun_copy_status.assert_called_with( - SRC_VOL['id']) - self.assertTrue(result) - - @mock.patch.object(context, 'get_admin_context') - @mock.patch.object(volume_types, 'get_volume_type') - def test_get_volume_type_extra_spec(self, - m_get_volume_type, - m_get_admin_context): - """Volume_type extra specs are found successfully.""" - vol = VOLUME.copy() - vol['volume_type_id'] = 1 - volume_type = {'extra_specs': {'override:test_key': 'test_value'}} - - m_get_admin_context.return_value = None - m_get_volume_type.return_value = volume_type - - result = self.driver._get_volume_type_extra_spec(vol, 'test_key') - - m_get_admin_context.assert_called_with() - m_get_volume_type.assert_called_with(None, vol['volume_type_id']) - self.assertEqual('test_value', result) - - @mock.patch.object(context, 'get_admin_context') - @mock.patch.object(volume_types, 'get_volume_type') - def test_get_violin_extra_spec(self, - m_get_volume_type, - m_get_admin_context): - """Volume_type extra specs are found successfully.""" - vol = VOLUME.copy() - vol['volume_type_id'] = 1 - volume_type = {'extra_specs': {'violin:test_key': 'test_value'}} - - m_get_admin_context.return_value = None - m_get_volume_type.return_value = volume_type - - result = self.driver._get_volume_type_extra_spec(vol, 'test_key') - - m_get_admin_context.assert_called_with() - m_get_volume_type.assert_called_with(None, vol['volume_type_id']) - self.assertEqual(result, 'test_value') - - def test_process_extra_specs_dedup(self): - '''Process the given extra specs and fill the required dict.''' - vol = VOLUME.copy() - vol['volume_type_id'] = 1 - spec_dict = { - 'pool_type': 'dedup', - 'size_mb': 205, - 'thick': False, - 'dedup': True, - 'thin': True} - - self.driver.vmem_mg = self.setup_mock_concerto() - self.driver._get_volume_type_extra_spec = mock.Mock( - return_value="True") - - result = self.driver._process_extra_specs(vol) - self.assertEqual(spec_dict, result) - - def test_process_extra_specs_no_specs(self): - '''Fill the required spec_dict in the absence of extra specs.''' - vol = VOLUME.copy() - spec_dict = { - 'pool_type': 'thick', - 'size_mb': 2048, - 'thick': True, - 'dedup': False, - 'thin': False} - - self.driver.vmem_mg = self.setup_mock_concerto() - self.driver._get_volume_type_extra_spec = mock.Mock( - return_value="False") - - result = self.driver._process_extra_specs(vol) - self.assertEqual(spec_dict, result) - - def test_process_extra_specs_no_specs_thin(self): - '''Fill the required spec_dict in the absence of extra specs.''' - vol = VOLUME.copy() - spec_dict = { - 'pool_type': 'thin', - 'size_mb': 205, - 'thick': False, - 'dedup': False, - 'thin': True} - - self.driver.vmem_mg = self.setup_mock_concerto() - self.driver._get_volume_type_extra_spec = mock.Mock( - return_value="False") - - save_thin = self.conf.san_thin_provision - self.conf.san_thin_provision = True - result = self.driver._process_extra_specs(vol) - self.assertEqual(spec_dict, result) - self.conf.san_thin_provision = save_thin - - def test_process_extra_specs_thin(self): - '''Fill the required spec_dict in the absence of extra specs.''' - vol = VOLUME.copy() - vol['volume_type_id'] = 1 - spec_dict = { - 'pool_type': 'thin', - 'size_mb': 205, - 'thick': False, - 'dedup': False, - 'thin': True} - - self.driver.vmem_mg = self.setup_mock_concerto() - self.driver._get_volume_type_extra_spec = mock.Mock( - side_effect=["True", "False"]) - - result = self.driver._process_extra_specs(vol) - self.assertEqual(spec_dict, result) - - def test_get_storage_pool_with_extra_specs(self): - '''Select a suitable pool based on specified extra specs.''' - vol = VOLUME.copy() - vol['volume_type_id'] = 1 - pool_type = "thick" - - selected_pool = { - 'storage_pool': 'StoragePoolA', - 'storage_pool_id': 99, - 'dedup': False, - 'thin': False} - - conf = { - 'pool.select_storage_pool.return_value': selected_pool, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._get_violin_extra_spec = mock.Mock( - return_value="StoragePoolA", - ) - - result = self.driver._get_storage_pool( - vol, - 100, - pool_type, - "create_lun") - - self.assertEqual(result, selected_pool) - - def test_get_storage_pool_configured_pools(self): - '''Select a suitable pool based on configured pools.''' - vol = VOLUME.copy() - pool_type = "dedup" - - self.conf.violin_dedup_only_pools = ['PoolA', 'PoolB'] - self.conf.violin_dedup_capable_pools = ['PoolC', 'PoolD'] - - selected_pool = { - 'dedup': True, - 'storage_pool': 'PoolA', - 'storage_pool_id': 123, - 'thin': True, - } - - conf = { - 'pool.select_storage_pool.return_value': selected_pool, - } - - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - self.driver._get_violin_extra_spec = mock.Mock( - return_value="StoragePoolA") - - result = self.driver._get_storage_pool( - vol, - 100, - pool_type, - "create_lun", - ) - - self.assertEqual(result, selected_pool) - self.driver.vmem_mg.pool.select_storage_pool.assert_called_with( - 100, - pool_type, - None, - self.conf.violin_dedup_only_pools, - self.conf.violin_dedup_capable_pools, - "random", - "create_lun", - ) - - def test_get_volume_stats(self): - '''Getting stats works successfully.''' - - self.conf.reserved_percentage = 0 - - expected_answers = { - 'vendor_name': 'Violin Memory, Inc.', - 'reserved_percentage': 0, - 'QoS_support': False, - 'free_capacity_gb': 2781, - 'total_capacity_gb': 14333, - 'consistencygroup_support': False, - } - owner = 'lab-host1' - - def lookup(value): - return six.text_type(value) + '.vmem.com' - conf = { - 'pool.get_storage_pools.return_value': STATS_STORAGE_POOL_RESPONSE, - } - self.driver.vmem_mg = self.setup_mock_concerto(m_conf=conf) - - with mock.patch('socket.getfqdn', side_effect=lookup): - result = self.driver._get_volume_stats(owner) - - self.assertDictEqual(expected_answers, result) diff --git a/cinder/tests/unit/volume/drivers/violin/test_v7000_fcp.py b/cinder/tests/unit/volume/drivers/violin/test_v7000_fcp.py deleted file mode 100644 index fe45585bcca..00000000000 --- a/cinder/tests/unit/volume/drivers/violin/test_v7000_fcp.py +++ /dev/null @@ -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)) diff --git a/cinder/volume/drivers/violin/__init__.py b/cinder/volume/drivers/violin/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/cinder/volume/drivers/violin/v7000_common.py b/cinder/volume/drivers/violin/v7000_common.py deleted file mode 100644 index c2d4b17cbbd..00000000000 --- a/cinder/volume/drivers/violin/v7000_common.py +++ /dev/null @@ -1,1091 +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 Memory 7000 Series All-Flash Array Common Driver for OpenStack Cinder - -Provides common (ie., non-protocol specific) management functions for -V7000 series flash arrays. - -Backend array communication is handled via VMEM's python library -called 'vmemclient'. - -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. -""" - -import math -import re -import six -import socket -import time - -from oslo_config import cfg -from oslo_log import log as logging -from oslo_service import loopingcall -from oslo_utils import units - -from cinder import context -from cinder.db.sqlalchemy import api -from cinder import exception -from cinder.i18n import _ -from cinder import utils -from cinder.volume import configuration -from cinder.volume import volume_types - - -LOG = logging.getLogger(__name__) - -try: - import vmemclient -except ImportError: - vmemclient = None -else: - LOG.info("Running with vmemclient version: %s", - vmemclient.__version__) - - -CONCERTO_SUPPORTED_VERSION_PATTERNS = ['Version 7.[0-9].?[0-9]?'] -CONCERTO_DEFAULT_PRIORITY = 'medium' -CONCERTO_DEFAULT_SRA_POLICY = 'preserveAll' -CONCERTO_DEFAULT_SRA_ENABLE_EXPANSION = True -CONCERTO_DEFAULT_SRA_EXPANSION_THRESHOLD = 50 -CONCERTO_DEFAULT_SRA_EXPANSION_INCREMENT = '1024MB' -CONCERTO_DEFAULT_SRA_EXPANSION_MAX_SIZE = None -CONCERTO_DEFAULT_SRA_ENABLE_SHRINK = False -CONCERTO_DEFAULT_POLICY_MAX_SNAPSHOTS = 1000 -CONCERTO_DEFAULT_POLICY_RETENTION_MODE = 'All' -CONCERTO_LUN_TYPE_THICK = 'THICK' -LUN_ALLOC_SZ = 10 - -violin_opts = [ - cfg.IntOpt('violin_request_timeout', - default=300, - help='Global backend request timeout, in seconds.'), - cfg.ListOpt('violin_dedup_only_pools', - default=[], - help='Storage pools to be used to setup dedup luns only.' - '(Comma separated list)'), - cfg.ListOpt('violin_dedup_capable_pools', - default=[], - help='Storage pools capable of dedup and other luns.' - '(Comma separated list)'), - cfg.StrOpt('violin_pool_allocation_method', - default='random', - choices=['random', 'largest', 'smallest'], - help='Method of choosing a storage pool for a lun.'), - cfg.ListOpt('violin_iscsi_target_ips', - default=[], - help='Target iSCSI addresses to use.' - '(Comma separated list)'), -] - -CONF = cfg.CONF -CONF.register_opts(violin_opts, group=configuration.SHARED_CONF_GROUP) - - -class V7000Common(object): - """Contains common code for the Violin V7000 drivers.""" - - def __init__(self, config): - self.vmem_mg = None - self.container = "" - self.config = config - - def do_setup(self, context): - """Any initialization the driver does while starting.""" - if not self.config.san_ip: - raise exception.InvalidInput( - reason=_('Gateway VIP is not set')) - - if vmemclient: - self.vmem_mg = vmemclient.open(self.config.san_ip, - self.config.san_login, - self.config.san_password, - keepalive=True) - - if self.vmem_mg is None: - msg = _('Failed to connect to array') - raise exception.VolumeBackendAPIException(data=msg) - - if self.vmem_mg.utility.is_external_head: - # With an external storage pool configuration is a must - if (self.config.violin_dedup_only_pools == [] and - self.config.violin_dedup_capable_pools == []): - - LOG.warning("Storage pools not configured.") - raise exception.InvalidInput( - reason=_('Storage pool configuration is ' - 'mandatory for external head')) - - def check_for_setup_error(self): - """Returns an error if prerequisites aren't met.""" - if vmemclient is None: - msg = _('vmemclient python library not found') - raise exception.VolumeBackendAPIException(data=msg) - - LOG.info("CONCERTO version: %s", self.vmem_mg.version) - - if not self._is_supported_vmos_version(self.vmem_mg.version): - msg = _('CONCERTO version is not supported') - raise exception.ViolinInvalidBackendConfig(reason=msg) - - @utils.synchronized('vmem-lun') - def _create_lun(self, volume): - """Creates a new lun. - - :param volume: volume object provided by the Manager - """ - spec_dict = {} - selected_pool = {} - - size_mb = volume['size'] * units.Ki - full_size_mb = size_mb - - LOG.debug("Creating LUN %(name)s, %(size)s MB.", - {'name': volume['name'], 'size': size_mb}) - - spec_dict = self._process_extra_specs(volume) - - try: - selected_pool = self._get_storage_pool( - volume, - size_mb, - spec_dict['pool_type'], - "create_lun") - - except exception.ViolinResourceNotFound: - raise - except Exception: - msg = _('No suitable storage pool found') - LOG.exception(msg) - raise exception.ViolinBackendErr(message=msg) - - try: - # Note: In the following create_lun command for setting up a dedup - # or thin lun the size_mb parameter is ignored and 10% of the - # full_size_mb specified is the size actually allocated to - # the lun. full_size_mb is the size the lun is allowed to - # grow. On the other hand, if it is a thick lun, the - # full_size_mb is ignored and size_mb is the actual - # allocated size of the lun. - - self._send_cmd(self.vmem_mg.lun.create_lun, - "Create resource successfully.", - volume['id'], - spec_dict['size_mb'], - selected_pool['dedup'], - selected_pool['thin'], - full_size_mb, - storage_pool_id=selected_pool['storage_pool_id']) - - except exception.ViolinBackendErrExists: - LOG.debug("Lun %s already exists, continuing.", volume['id']) - - except Exception: - LOG.exception("Lun create for %s failed!", volume['id']) - raise - - @utils.synchronized('vmem-lun') - def _delete_lun(self, volume): - """Deletes a lun. - - :param volume: volume object provided by the Manager - """ - success_msgs = ['Delete resource successfully', ''] - - LOG.debug("Deleting lun %s.", volume['id']) - - # If the LUN has ever had a snapshot, it has an SRA and - # policy that must be deleted first. - self._delete_lun_snapshot_bookkeeping(volume['id']) - - try: - self._send_cmd(self.vmem_mg.lun.delete_lun, - success_msgs, volume['id']) - - except vmemclient.core.error.NoMatchingObjectIdError: - LOG.debug("Lun %s already deleted, continuing.", volume['id']) - - except exception.ViolinBackendErrExists: - LOG.exception("Lun %s has dependent snapshots, " - "skipping lun deletion.", volume['id']) - raise exception.VolumeIsBusy(volume_name=volume['id']) - - except Exception: - LOG.exception("Lun delete for %s failed!", volume['id']) - raise - - def _extend_lun(self, volume, new_size): - """Extend an existing volume's size. - - :param volume: volume object provided by the Manager - :param new_size: new size in GB to be applied - """ - v = self.vmem_mg - - typeid = volume['volume_type_id'] - - if typeid and not self.vmem_mg.utility.is_external_head: - spec_value = self._get_volume_type_extra_spec(volume, "dedup") - if spec_value and spec_value.lower() == "true": - # A Dedup lun's size cannot be modified in Concerto. - msg = _('Dedup luns cannot be extended') - raise exception.VolumeDriverException(message=msg) - - size_mb = volume['size'] * units.Ki - new_size_mb = new_size * units.Ki - - # Concerto lun extend requires number of MB to increase size by, - # not the final size value. - # - delta_mb = new_size_mb - size_mb - - LOG.debug("Extending lun %(id)s, from %(size)s to %(new_size)s MB.", - {'id': volume['id'], 'size': size_mb, - 'new_size': new_size_mb}) - - try: - self._send_cmd(v.lun.extend_lun, - "Expand resource successfully", - volume['id'], delta_mb) - - except Exception: - LOG.exception("LUN extend failed!") - raise - - def _create_lun_snapshot(self, snapshot): - """Create a new cinder snapshot on a volume. - - This maps onto a Concerto 'timemark', but we must always first - ensure that a snapshot resource area (SRA) exists, and that a - snapshot policy exists. - - :param snapshot: cinder snapshot object provided by the Manager - - :raises: - VolumeBackendAPIException: If SRA could not be created, or - snapshot policy could not be created - RequestRetryTimeout: If backend could not complete the request - within the allotted timeout. - ViolinBackendErr: If backend reports an error during the - create snapshot phase. - """ - - cinder_volume_id = snapshot['volume_id'] - cinder_snapshot_id = snapshot['id'] - - LOG.debug("Creating LUN snapshot %(snap_id)s on volume " - "%(vol_id)s %(dpy_name)s.", - {'snap_id': cinder_snapshot_id, - 'vol_id': cinder_volume_id, - 'dpy_name': snapshot['display_name']}) - - self._ensure_snapshot_resource_area(cinder_volume_id) - - self._ensure_snapshot_policy(cinder_volume_id) - - try: - self._send_cmd( - self.vmem_mg.snapshot.create_lun_snapshot, - "Create TimeMark successfully", - lun=cinder_volume_id, - comment=self._compress_snapshot_id(cinder_snapshot_id), - priority=CONCERTO_DEFAULT_PRIORITY, - enable_notification=False) - except Exception: - LOG.exception("Lun create snapshot for " - "volume %(vol)s snapshot %(snap)s failed!", - {'vol': cinder_volume_id, - 'snap': cinder_snapshot_id}) - raise - - def _delete_lun_snapshot(self, snapshot): - """Delete the specified cinder snapshot. - - :param snapshot: cinder snapshot object provided by the Manager - - :raises: - RequestRetryTimeout: If backend could not complete the request - within the allotted timeout. - ViolinBackendErr: If backend reports an error during the - delete snapshot phase. - """ - LOG.debug("Deleting snapshot %(snap_id)s on volume " - "%(vol_id)s %(dpy_name)s", - {'snap_id': snapshot['id'], - 'vol_id': snapshot['volume_id'], - 'dpy_name': snapshot['display_name']}) - - return self._wait_run_delete_lun_snapshot(snapshot) - - def _create_volume_from_snapshot(self, snapshot, volume): - """Create a new cinder volume from a given snapshot of a lun - - This maps onto a Concerto 'copy snapshot to lun'. Concerto - creates the lun and then copies the snapshot into it. - - :param snapshot: cinder snapshot object provided by the Manager - :param volume: cinder volume to be created - """ - cinder_volume_id = volume['id'] - cinder_snapshot_id = snapshot['id'] - size_mb = volume['size'] * units.Ki - result = None - spec_dict = {} - - LOG.debug("Copying snapshot %(snap_id)s onto volume %(vol_id)s " - "%(dpy_name)s", - {'snap_id': cinder_snapshot_id, - 'vol_id': cinder_volume_id, - 'dpy_name': snapshot['display_name']}) - - source_lun_info = self.vmem_mg.lun.get_lun_info(snapshot['volume_id']) - self._validate_lun_type_for_copy(source_lun_info['subType']) - - spec_dict = self._process_extra_specs(volume) - try: - selected_pool = self._get_storage_pool(volume, - size_mb, - spec_dict['pool_type'], - "create_lun") - - except exception.ViolinResourceNotFound: - raise - except Exception: - msg = _('No suitable storage pool found') - LOG.exception(msg) - raise exception.ViolinBackendErr(message=msg) - - try: - result = self.vmem_mg.lun.copy_snapshot_to_new_lun( - source_lun=snapshot['volume_id'], - source_snapshot_comment=self._compress_snapshot_id( - cinder_snapshot_id), - destination=cinder_volume_id, - storage_pool_id=selected_pool['storage_pool_id']) - - if not result['success']: - self._check_error_code(result) - - except Exception: - LOG.exception("Copy snapshot to volume for " - "snapshot %(snap)s volume %(vol)s failed!", - {'snap': cinder_snapshot_id, - 'vol': cinder_volume_id}) - raise - - # get the destination lun info and extract virtualdeviceid - info = self.vmem_mg.lun.get_lun_info(object_id=result['object_id']) - - self._wait_for_lun_or_snap_copy( - snapshot['volume_id'], dest_vdev_id=info['virtualDeviceID']) - - def _create_lun_from_lun(self, src_vol, dest_vol): - """Copy the contents of a lun to a new lun (i.e., full clone). - - :param src_vol: cinder volume to clone - :param dest_vol: cinder volume to be created - """ - size_mb = dest_vol['size'] * units.Ki - src_vol_mb = src_vol['size'] * units.Ki - result = None - spec_dict = {} - - LOG.debug("Copying lun %(src_vol_id)s onto lun %(dest_vol_id)s.", - {'src_vol_id': src_vol['id'], - 'dest_vol_id': dest_vol['id']}) - - try: - source_lun_info = self.vmem_mg.lun.get_lun_info(src_vol['id']) - self._validate_lun_type_for_copy(source_lun_info['subType']) - - # in order to do a full clone the source lun must have a - # snapshot resource - self._ensure_snapshot_resource_area(src_vol['id']) - - spec_dict = self._process_extra_specs(dest_vol) - selected_pool = self._get_storage_pool(dest_vol, - size_mb, - spec_dict['pool_type'], - None) - - result = self.vmem_mg.lun.copy_lun_to_new_lun( - source=src_vol['id'], destination=dest_vol['id'], - storage_pool_id=selected_pool['storage_pool_id']) - - if not result['success']: - self._check_error_code(result) - - except Exception: - LOG.exception("Create new lun from lun for source " - "%(src)s => destination %(dest)s failed!", - {'src': src_vol['id'], 'dest': dest_vol['id']}) - raise - - self._wait_for_lun_or_snap_copy( - src_vol['id'], dest_obj_id=result['object_id']) - - # extend the copied lun if requested size is larger then original - if size_mb > src_vol_mb: - # dest_vol size has to be set to reflect what it is currently - dest_vol_size = dest_vol['size'] - dest_vol['size'] = src_vol['size'] - self._extend_lun(dest_vol, dest_vol_size) - dest_vol['size'] = dest_vol_size - - def _send_cmd(self, request_func, success_msgs, *args, **kwargs): - """Run an XG request function, and retry as needed. - - The request will be retried until it returns a success - message, a failure message, or the global request timeout is - hit. - - This wrapper is meant to deal with backend requests that can - fail for any variety of reasons, for instance, when the system - is already busy handling other LUN requests. If there is no - space left, or other "fatal" errors are returned (see - _fatal_error_code() for a list of all known error conditions). - - :param request_func: XG api method to call - :param success_msgs: Success messages expected from the backend - :param *args: argument array to be passed to the request_func - :param **kwargs: argument dictionary to be passed to request_func - :returns: the response dict from the last XG call - """ - resp = {} - start = time.time() - done = False - - if isinstance(success_msgs, six.string_types): - success_msgs = [success_msgs] - - while not done: - if time.time() - start >= self.config.violin_request_timeout: - raise exception.ViolinRequestRetryTimeout( - timeout=self.config.violin_request_timeout) - - resp = request_func(*args, **kwargs) - - if not resp['msg']: - # XG requests will return None for a message if no message - # string is passed in the raw response - resp['msg'] = '' - - for msg in success_msgs: - if resp['success'] and msg in resp['msg']: - done = True - break - - if not resp['success']: - self._check_error_code(resp) - done = True - break - - return resp - - def _send_cmd_and_verify(self, request_func, verify_func, - request_success_msgs='', rargs=None, vargs=None): - """Run an XG request function, retry if needed, and verify success. - - If the verification fails, then retry the request/verify cycle - until both functions are successful, the request function - returns a failure message, or the global request timeout is - hit. - - This wrapper is meant to deal with backend requests that can - fail for any variety of reasons, for instance, when the system - is already busy handling other LUN requests. It is also smart - enough to give up if clustering is down (eg no HA available), - there is no space left, or other "fatal" errors are returned - (see _fatal_error_code() for a list of all known error - conditions). - - :param request_func: XG api method to call - :param verify_func: function call to verify request was completed - :param request_success_msg: Success message expected for request_func - :param *rargs: argument array to be passed to request_func - :param *vargs: argument array to be passed to verify_func - :returns: the response dict from the last XG call - """ - resp = {} - start = time.time() - request_needed = True - verify_needed = True - - if isinstance(request_success_msgs, six.string_types): - request_success_msgs = [request_success_msgs] - - rargs = rargs if rargs else [] - vargs = vargs if vargs else [] - - while request_needed or verify_needed: - if time.time() - start >= self.config.violin_request_timeout: - raise exception.ViolinRequestRetryTimeout( - timeout=self.config.violin_request_timeout) - - if request_needed: - resp = request_func(*rargs) - - if not resp['msg']: - # XG requests will return None for a message if no message - # string is passed in the raw response - resp['msg'] = '' - - for msg in request_success_msgs: - if resp['success'] and msg in resp['msg']: - request_needed = False - break - - if not resp['success']: - self._check_error_code(resp) - request_needed = False - - elif verify_needed: - success = verify_func(*vargs) - if success: - # XG verify func was completed - verify_needed = False - - return resp - - def _ensure_snapshot_resource_area(self, volume_id): - """Make sure concerto snapshot resource area exists on volume. - - :param volume_id: Cinder volume ID corresponding to the backend LUN - - :raises: - VolumeBackendAPIException: if cinder volume does not exist - on backnd, or SRA could not be created. - """ - ctxt = context.get_admin_context() - volume = api.volume_get(ctxt, volume_id) - spec_dict = {} - - if not volume: - msg = (_("Failed to ensure snapshot resource area, could not " - "locate volume for id %s") % volume_id) - raise exception.VolumeBackendAPIException(data=msg) - - if not self.vmem_mg.snapshot.lun_has_a_snapshot_resource( - lun=volume_id): - # Per Concerto documentation, the SRA size should be computed - # as follows - # Size-of-original-LUN Reserve for SRA - # < 500MB 100% - # 500MB to 2G 50% - # >= 2G 20% - # Note: cinder volume.size is in GB, vmemclient wants MB. - lun_size_mb = volume['size'] * units.Ki - if lun_size_mb < 500: - snap_size_mb = lun_size_mb - elif lun_size_mb < 2000: - snap_size_mb = 0.5 * lun_size_mb - else: - snap_size_mb = 0.2 * lun_size_mb - - snap_size_mb = int(math.ceil(snap_size_mb)) - - spec_dict = self._process_extra_specs(volume) - - try: - selected_pool = self._get_storage_pool( - volume, - snap_size_mb, - spec_dict['pool_type'], - None) - - LOG.debug("Creating SRA of %(ssmb)sMB for lun of %(lsmb)sMB " - "on %(vol_id)s", - {'ssmb': snap_size_mb, - 'lsmb': lun_size_mb, - 'vol_id': volume_id}) - - except exception.ViolinResourceNotFound: - raise - except Exception: - msg = _('No suitable storage pool found') - LOG.exception(msg) - raise exception.ViolinBackendErr(message=msg) - - res = self.vmem_mg.snapshot.create_snapshot_resource( - lun=volume_id, - size=snap_size_mb, - enable_notification=False, - policy=CONCERTO_DEFAULT_SRA_POLICY, - enable_expansion=CONCERTO_DEFAULT_SRA_ENABLE_EXPANSION, - expansion_threshold=CONCERTO_DEFAULT_SRA_EXPANSION_THRESHOLD, - expansion_increment=CONCERTO_DEFAULT_SRA_EXPANSION_INCREMENT, - expansion_max_size=CONCERTO_DEFAULT_SRA_EXPANSION_MAX_SIZE, - enable_shrink=CONCERTO_DEFAULT_SRA_ENABLE_SHRINK, - storage_pool_id=selected_pool['storage_pool_id']) - - if (not res['success']): - msg = (_("Failed to create snapshot resource area on " - "volume %(vol)s: %(res)s.") % - {'vol': volume_id, 'res': res['msg']}) - raise exception.VolumeBackendAPIException(data=msg) - - def _ensure_snapshot_policy(self, volume_id): - """Ensure concerto snapshot policy exists on cinder volume. - - A snapshot policy is required by concerto in order to create snapshots. - - :param volume_id: Cinder volume ID corresponding to the backend LUN - - :raises: - VolumeBackendAPIException: when snapshot policy cannot be created. - """ - if not self.vmem_mg.snapshot.lun_has_a_snapshot_policy( - lun=volume_id): - - res = self.vmem_mg.snapshot.create_snapshot_policy( - lun=volume_id, - max_snapshots=CONCERTO_DEFAULT_POLICY_MAX_SNAPSHOTS, - enable_replication=False, - enable_snapshot_schedule=False, - enable_cdp=False, - retention_mode=CONCERTO_DEFAULT_POLICY_RETENTION_MODE) - - if not res['success']: - msg = (_( - "Failed to create snapshot policy on " - "volume %(vol)s: %(res)s.") % - {'vol': volume_id, 'res': res['msg']}) - raise exception.VolumeBackendAPIException(data=msg) - - def _delete_lun_snapshot_bookkeeping(self, volume_id): - """Clear residual snapshot support resources from LUN. - - :raises: - VolumeBackendAPIException: If snapshots still exist on the LUN. - """ - - # Make absolutely sure there are no snapshots present - try: - snaps = self.vmem_mg.snapshot.get_snapshots(volume_id) - if len(snaps) > 0: - msg = (_("Cannot delete LUN %s while snapshots exist.") % - volume_id) - raise exception.VolumeBackendAPIException(data=msg) - except vmemclient.core.error.NoMatchingObjectIdError: - pass - except vmemclient.core.error.MissingParameterError: - pass - - try: - res = self.vmem_mg.snapshot.delete_snapshot_policy( - lun=volume_id) - if not res['success']: - if 'TimeMark is disabled' in res['msg']: - LOG.debug("Verified no snapshot policy is on volume %s.", - volume_id) - else: - msg = (_("Unable to delete snapshot policy on " - "volume %s.") % volume_id) - raise exception.VolumeBackendAPIException(data=msg) - else: - LOG.debug("Deleted snapshot policy on volume " - "%(vol)s, result %(res)s.", - {'vol': volume_id, 'res': res}) - except vmemclient.core.error.NoMatchingObjectIdError: - LOG.debug("Verified no snapshot policy present on volume %s.", - volume_id) - pass - - try: - res = self.vmem_mg.snapshot.delete_snapshot_resource( - lun=volume_id) - LOG.debug("Deleted snapshot resource area on " - "volume %(vol)s, result %(res)s.", - {'vol': volume_id, 'res': res}) - except vmemclient.core.error.NoMatchingObjectIdError: - LOG.debug("Verified no snapshot resource area present on " - "volume %s.", volume_id) - pass - - def _compress_snapshot_id(self, cinder_snap_id): - """Compress cinder snapshot ID so it fits in backend. - - Compresses to fit in 32-chars. - """ - return ''.join(six.text_type(cinder_snap_id).split('-')) - - def _get_snapshot_from_lun_snapshots( - self, cinder_volume_id, cinder_snap_id): - """Locate backend snapshot dict associated with cinder snapshot id. - - :returns: Cinder snapshot dictionary if found, None otherwise. - """ - - try: - snaps = self.vmem_mg.snapshot.get_snapshots(cinder_volume_id) - except vmemclient.core.error.NoMatchingObjectIdError: - return None - - key = self._compress_snapshot_id(cinder_snap_id) - - for s in snaps: - if s['comment'] == key: - # Remap return dict to its uncompressed form - s['comment'] = cinder_snap_id - return s - - def _wait_for_lun_or_snap_copy(self, src_vol_id, dest_vdev_id=None, - dest_obj_id=None): - """Poll to see when a lun or snap copy to a lun is complete. - - :param src_vol_id: cinder volume ID of source volume - :param dest_vdev_id: virtual device ID of destination, for snap copy - :param dest_obj_id: lun object ID of destination, for lun copy - :returns: True if successful, False otherwise - """ - wait_id = None - wait_func = None - - if dest_vdev_id: - wait_id = dest_vdev_id - wait_func = self.vmem_mg.snapshot.get_snapshot_copy_status - elif dest_obj_id: - wait_id = dest_obj_id - wait_func = self.vmem_mg.lun.get_lun_copy_status - else: - return False - - def _loop_func(): - LOG.debug("Entering _wait_for_lun_or_snap_copy loop: " - "vdev=%s, objid=%s", dest_vdev_id, dest_obj_id) - - target_id, mb_copied, percent = wait_func(src_vol_id) - - if target_id is None: - # pre-copy transient result - LOG.debug("lun or snap copy prepping.") - pass - elif target_id != wait_id: - # the copy is complete, another lun is being copied - LOG.debug("lun or snap copy complete.") - raise loopingcall.LoopingCallDone(retvalue=True) - elif mb_copied is not None: - # copy is in progress - LOG.debug("MB copied: %(copied)d, percent done: %(percent)d.", - {'copied': mb_copied, 'percent': percent}) - pass - elif percent == 0: - # copy has just started - LOG.debug("lun or snap copy started.") - pass - elif percent == 100: - # copy is complete - LOG.debug("lun or snap copy complete.") - raise loopingcall.LoopingCallDone(retvalue=True) - else: - # unexpected case - LOG.debug("unexpected case (%(id)s, %(bytes)s, %(percent)s)", - {'id': target_id, - 'bytes': mb_copied, - 'percent': six.text_type(percent)}) - raise loopingcall.LoopingCallDone(retvalue=False) - - timer = loopingcall.FixedIntervalLoopingCall(_loop_func) - success = timer.start(interval=1).wait() - - return success - - def _is_supported_vmos_version(self, version_string): - """Check a version string for compatibility with OpenStack. - - Compare a version string against the global regex of versions - compatible with OpenStack. - - :param version_string: array's gateway version string - :returns: True if supported, false if not - """ - for pattern in CONCERTO_SUPPORTED_VERSION_PATTERNS: - if re.match(pattern, version_string): - return True - return False - - def _check_error_code(self, response): - """Raise an exception when backend returns certain errors. - - Error codes returned from the backend have to be examined - individually. Not all of them are fatal. For example, lun attach - failing becase the client is already attached is not a fatal error. - - :param response: a response dict result from the vmemclient request - """ - if "Error: 0x9001003c" in response['msg']: - # This error indicates a duplicate attempt to attach lun, - # non-fatal error - pass - elif "Error: 0x9002002b" in response['msg']: - # lun unexport failed - lun is not exported to any clients, - # non-fatal error - pass - elif "Error: 0x09010023" in response['msg']: - # lun delete failed - dependent snapshot copy in progress, - # fatal error - raise exception.ViolinBackendErr(message=response['msg']) - elif "Error: 0x09010048" in response['msg']: - # lun delete failed - dependent snapshots still exist, - # fatal error - raise exception.ViolinBackendErr(message=response['msg']) - elif "Error: 0x90010022" in response['msg']: - # lun create failed - lun with same name already exists, - # fatal error - raise exception.ViolinBackendErrExists() - elif "Error: 0x90010089" in response['msg']: - # lun export failed - lun is still being created as copy, - # fatal error - raise exception.ViolinBackendErr(message=response['msg']) - else: - # assume any other error is fatal - raise exception.ViolinBackendErr(message=response['msg']) - - def _get_volume_type_extra_spec(self, volume, spec_key): - """Parse data stored in a volume_type's extra_specs table. - - :param volume: volume object containing volume_type to query - :param spec_key: the metadata key to search for - :returns: string value associated with spec_key - """ - spec_value = None - ctxt = context.get_admin_context() - typeid = volume['volume_type_id'] - if typeid: - volume_type = volume_types.get_volume_type(ctxt, typeid) - volume_specs = volume_type.get('extra_specs') - for key, val in volume_specs.items(): - - # Strip the prefix "capabilities" - if ':' in key: - scope = key.split(':') - key = scope[1] - if key == spec_key: - spec_value = val - break - - return spec_value - - def _get_violin_extra_spec(self, volume, spec_key): - """Parse volume_type's extra_specs table for a violin-specific key. - - :param volume: volume object containing volume_type to query - :param spec_key: the metadata key to search for - :returns: string value associated with spec_key - """ - spec_value = None - ctxt = context.get_admin_context() - typeid = volume['volume_type_id'] - if typeid: - volume_type = volume_types.get_volume_type(ctxt, typeid) - volume_specs = volume_type.get('extra_specs') - for key, val in volume_specs.items(): - - # Strip the prefix "violin" - if ':' in key: - scope = key.split(':') - key = scope[1] - if scope[0] == "violin" and key == spec_key: - spec_value = val - break - return spec_value - - def _get_storage_pool(self, volume, size_in_mb, pool_type, usage): - # User-specified pool takes precedence over others - - pool = None - typeid = volume.get('volume_type_id') - if typeid: - # Extract the storage_pool name if one is specified - pool = self._get_violin_extra_spec(volume, "storage_pool") - - # Select a storage pool - selected_pool = self.vmem_mg.pool.select_storage_pool( - size_in_mb, - pool_type, - pool, - self.config.violin_dedup_only_pools, - self.config.violin_dedup_capable_pools, - self.config.violin_pool_allocation_method, - usage) - if selected_pool is None: - # Backend has not provided a suitable storage pool - msg = _("Backend does not have a suitable storage pool.") - raise exception.ViolinResourceNotFound(message=msg) - - LOG.debug("Storage pool returned is %s", - selected_pool['storage_pool']) - - return selected_pool - - def _process_extra_specs(self, volume): - spec_dict = {} - thin_lun = False - thick_lun = False - dedup = False - size_mb = volume['size'] * units.Ki - full_size_mb = size_mb - - if self.config.san_thin_provision: - thin_lun = True - # Set the actual allocation size for thin lun - # default here is 10% - size_mb = int(math.ceil(float(size_mb) / LUN_ALLOC_SZ)) - - typeid = volume.get('volume_type_id') - if typeid: - # extra_specs with thin specified overrides san_thin_provision - spec_value = self._get_volume_type_extra_spec(volume, "thin") - if not thin_lun and spec_value and spec_value.lower() == "true": - thin_lun = True - # Set the actual allocation size for thin lun - # default here is 10% - size_mb = int(math.ceil(float(size_mb) / LUN_ALLOC_SZ)) - - # Set thick lun before checking for dedup, - # since dedup is always thin - if not thin_lun: - thick_lun = True - - spec_value = self._get_volume_type_extra_spec(volume, "dedup") - if spec_value and spec_value.lower() == "true": - dedup = True - # A dedup lun is always a thin lun - thin_lun = True - thick_lun = False - # Set the actual allocation size for thin lun - # default here is 10%. The actual allocation may - # different, depending on other factors - size_mb = int(math.ceil(float(full_size_mb) / LUN_ALLOC_SZ)) - - if dedup: - spec_dict['pool_type'] = "dedup" - elif thin_lun: - spec_dict['pool_type'] = "thin" - else: - spec_dict['pool_type'] = "thick" - thick_lun = True - - spec_dict['size_mb'] = size_mb - spec_dict['thick'] = thick_lun - spec_dict['thin'] = thin_lun - spec_dict['dedup'] = dedup - - return spec_dict - - def _get_volume_stats(self, san_ip): - """Gathers array stats and converts them to GB values.""" - free_gb = 0 - total_gb = 0 - - owner = socket.getfqdn(san_ip) - # Store DNS lookups to prevent asking the same question repeatedly - owner_lookup = {san_ip: owner} - pools = self.vmem_mg.pool.get_storage_pools( - verify=True, - include_full_info=True, - ) - - for short_info, full_info in pools: - mod = '' - pool_free_mb = 0 - pool_total_mb = 0 - for dev in full_info.get('physicaldevices', []): - if dev['owner'] not in owner_lookup: - owner_lookup[dev['owner']] = socket.getfqdn(dev['owner']) - if owner_lookup[dev['owner']] == owner: - pool_free_mb += dev['availsize_mb'] - pool_total_mb += dev['size_mb'] - elif not mod: - mod = ' *' - LOG.debug('pool %(pool)s: %(avail)s / %(total)s MB free %(mod)s', - {'pool': short_info['name'], 'avail': pool_free_mb, - 'total': pool_total_mb, 'mod': mod}) - free_gb += int(pool_free_mb / units.Ki) - total_gb += int(pool_total_mb / units.Ki) - - data = { - 'vendor_name': 'Violin Memory, Inc.', - 'reserved_percentage': self.config.reserved_percentage, - 'QoS_support': False, - 'free_capacity_gb': free_gb, - 'total_capacity_gb': total_gb, - 'consistencygroup_support': False, - } - - return data - - def _wait_run_delete_lun_snapshot(self, snapshot): - """Run and wait for LUN snapshot to complete. - - :param snapshot -- cinder snapshot object provided by the Manager - """ - cinder_volume_id = snapshot['volume_id'] - cinder_snapshot_id = snapshot['id'] - - comment = self._compress_snapshot_id(cinder_snapshot_id) - oid = self.vmem_mg.snapshot.snapshot_comment_to_object_id( - cinder_volume_id, comment) - - def _loop_func(): - LOG.debug("Entering _wait_run_delete_lun_snapshot loop: " - "vol=%(vol)s, snap_id=%(snap_id)s, oid=%(oid)s", - {'vol': cinder_volume_id, - 'oid': oid, - 'snap_id': cinder_snapshot_id}) - - try: - ans = self.vmem_mg.snapshot.delete_lun_snapshot( - snapshot_object_id=oid) - except Exception: - msg = (_("Failed to delete snapshot " - "%(snap)s of volume %(vol)s") % - {'snap': cinder_snapshot_id, 'vol': cinder_volume_id}) - raise exception.ViolinBackendErr(msg) - - if ans['success']: - LOG.debug("Delete snapshot %(snap_id)s of %(vol)s: " - "success", {'vol': cinder_volume_id, - 'snap_id': cinder_snapshot_id}) - raise loopingcall.LoopingCallDone(retvalue=True) - else: - LOG.warning("Delete snapshot %(snap)s of %(vol)s " - "encountered temporary error: %(msg)s", - {'snap': cinder_snapshot_id, - 'vol': cinder_volume_id, - 'msg': ans['msg']}) - - timer = loopingcall.FixedIntervalLoopingCall(_loop_func) - success = timer.start(interval=1).wait() - - return success - - def _validate_lun_type_for_copy(self, lun_type): - """Make sure volume type is thick. - - :param lun_type: Cinder volume type - - :raises: - VolumeBackendAPIException: if volume type is not thick, - copying the lun is not possible. - """ - if lun_type != CONCERTO_LUN_TYPE_THICK: - msg = _('Lun copy currently only supported for thick luns') - LOG.error(msg) - raise exception.ViolinBackendErr(message=msg) diff --git a/cinder/volume/drivers/violin/v7000_fcp.py b/cinder/volume/drivers/violin/v7000_fcp.py deleted file mode 100644 index e987b6cfdf4..00000000000 --- a/cinder/volume/drivers/violin/v7000_fcp.py +++ /dev/null @@ -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 diff --git a/cinder/volume/drivers/violin/v7000_iscsi.py b/cinder/volume/drivers/violin/v7000_iscsi.py deleted file mode 100644 index f64455ddb0e..00000000000 --- a/cinder/volume/drivers/violin/v7000_iscsi.py +++ /dev/null @@ -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 - 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) diff --git a/doc/source/configuration/block-storage/drivers/violin-v7000-driver.rst b/doc/source/configuration/block-storage/drivers/violin-v7000-driver.rst deleted file mode 100644 index 69df6af0068..00000000000 --- a/doc/source/configuration/block-storage/drivers/violin-v7000-driver.rst +++ /dev/null @@ -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 - `__. - - .. 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. diff --git a/doc/source/configuration/block-storage/volume-drivers.rst b/doc/source/configuration/block-storage/volume-drivers.rst index 0fecc9ad97a..5c85c68428c 100644 --- a/doc/source/configuration/block-storage/volume-drivers.rst +++ b/doc/source/configuration/block-storage/volume-drivers.rst @@ -52,7 +52,6 @@ Volume drivers drivers/solidfire-volume-driver.rst drivers/synology-dsm-driver.rst drivers/tintri-volume-driver.rst - drivers/violin-v7000-driver.rst drivers/vzstorage-driver.rst drivers/vmware-vmdk-driver.rst drivers/windows-iscsi-volume-driver.rst diff --git a/doc/source/configuration/tables/cinder-violin.inc b/doc/source/configuration/tables/cinder-violin.inc deleted file mode 100644 index 703856e6fdb..00000000000 --- a/doc/source/configuration/tables/cinder-violin.inc +++ /dev/null @@ -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. diff --git a/releasenotes/notes/queens-driver-removal-72a1a36689b6d890.yaml b/releasenotes/notes/queens-driver-removal-72a1a36689b6d890.yaml index 59cfee3b3c8..a355f99c7fe 100644 --- a/releasenotes/notes/queens-driver-removal-72a1a36689b6d890.yaml +++ b/releasenotes/notes/queens-driver-removal-72a1a36689b6d890.yaml @@ -11,4 +11,5 @@ upgrade: * Infortrend * QNAP * Reduxio + * Violin