Merge "Create Seagate driver from dothill driver"
This commit is contained in:
@@ -149,6 +149,8 @@ from cinder.volume.drivers.san import san as cinder_volume_drivers_san_san
|
||||
from cinder.volume.drivers import sheepdog as cinder_volume_drivers_sheepdog
|
||||
from cinder.volume.drivers import solidfire as cinder_volume_drivers_solidfire
|
||||
from cinder.volume.drivers import storpool as cinder_volume_drivers_storpool
|
||||
from cinder.volume.drivers.stx import common as \
|
||||
cinder_volume_drivers_stx_common
|
||||
from cinder.volume.drivers.synology import synology_common as \
|
||||
cinder_volume_drivers_synology_synologycommon
|
||||
from cinder.volume.drivers.veritas_access import veritas_iscsi as \
|
||||
@@ -357,6 +359,8 @@ def list_opts():
|
||||
cinder_volume_drivers_san_san.san_opts,
|
||||
cinder_volume_drivers_sheepdog.sheepdog_opts,
|
||||
cinder_volume_drivers_solidfire.sf_opts,
|
||||
cinder_volume_drivers_stx_common.common_opts,
|
||||
cinder_volume_drivers_stx_common.iscsi_opts,
|
||||
cinder_volume_drivers_synology_synologycommon.cinder_opts,
|
||||
cinder_volume_drivers_vmware_vmdk.vmdk_opts,
|
||||
cinder_volume_drivers_vzstorage.vzstorage_opts,
|
||||
|
||||
+139
-127
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 DotHill Systems
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-19 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -14,22 +14,32 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
"""Unit tests for OpenStack Cinder DotHill driver."""
|
||||
|
||||
from defusedxml import lxml as etree
|
||||
import mock
|
||||
import requests
|
||||
"""Unit tests for OpenStack Cinder Seagate driver."""
|
||||
|
||||
from cinder import exception
|
||||
from cinder.objects import fields
|
||||
from cinder import test
|
||||
from cinder.volume.drivers.dothill import dothill_client as dothill
|
||||
from cinder.volume.drivers.dothill import dothill_common
|
||||
from cinder.volume.drivers.dothill import dothill_fc
|
||||
from cinder.volume.drivers.dothill import dothill_iscsi
|
||||
from cinder.volume.drivers.dothill import exception as dh_exception
|
||||
|
||||
from cinder.objects import fields
|
||||
|
||||
import cinder.volume.drivers.stx.client
|
||||
import cinder.volume.drivers.stx.common
|
||||
import cinder.volume.drivers.stx.exception as stx_exception
|
||||
import cinder.volume.drivers.stx.fc
|
||||
import cinder.volume.drivers.stx.iscsi
|
||||
|
||||
from cinder.zonemanager import utils as fczm_utils
|
||||
|
||||
from defusedxml import lxml as etree
|
||||
|
||||
import mock
|
||||
|
||||
import requests
|
||||
|
||||
STXClient = cinder.volume.drivers.stx.client.STXClient
|
||||
STXCommon = cinder.volume.drivers.stx.common.STXCommon
|
||||
STXFCDriver = cinder.volume.drivers.stx.fc.STXFCDriver
|
||||
STXISCSIDriver = cinder.volume.drivers.stx.iscsi.STXISCSIDriver
|
||||
|
||||
session_key = '12a1626754554a21d85040760c81b'
|
||||
resp_login = '''<RESPONSE><OBJECT basetype="status" name="status" oid="1">
|
||||
<PROPERTY name="response-type">success</PROPERTY>
|
||||
@@ -119,7 +129,7 @@ test_retype_volume = {'attach_status': fields.VolumeAttachStatus.DETACHED,
|
||||
'display_name': 'test volume', 'name': 'volume',
|
||||
'size': 10}
|
||||
test_host = {'capabilities': {'location_info':
|
||||
'DotHillVolumeDriver:xxxxx:dg02:A'}}
|
||||
'SeagateVolumeDriver:xxxxx:dg02:A'}}
|
||||
test_snap = {'id': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
'volume': {'name_id': None},
|
||||
'volume_id': vol_id, 'display_name': 'test volume',
|
||||
@@ -159,16 +169,16 @@ invalid_connector = {'ip': '10.0.0.2',
|
||||
'host': 'fakehost'}
|
||||
|
||||
|
||||
class TestDotHillClient(test.TestCase):
|
||||
class TestSeagateClient(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TestDotHillClient, self).setUp()
|
||||
super(TestSeagateClient, self).setUp()
|
||||
self.login = 'manage'
|
||||
self.passwd = '!manage'
|
||||
self.ip = '10.0.0.1'
|
||||
self.protocol = 'http'
|
||||
self.ssl_verify = False
|
||||
self.client = dothill.DotHillClient(self.ip, self.login, self.passwd,
|
||||
self.protocol, self.ssl_verify)
|
||||
self.client = STXClient(self.ip, self.login, self.passwd,
|
||||
self.protocol, self.ssl_verify)
|
||||
|
||||
@mock.patch('requests.get')
|
||||
def test_login(self, mock_requests_get):
|
||||
@@ -176,7 +186,7 @@ class TestDotHillClient(test.TestCase):
|
||||
mock_requests_get.return_value = m
|
||||
|
||||
m.text.encode.side_effect = [resp_badlogin, resp_badlogin]
|
||||
self.assertRaises(dh_exception.DotHillAuthenticationError,
|
||||
self.assertRaises(stx_exception.AuthenticationError,
|
||||
self.client.login)
|
||||
|
||||
m.text.encode.side_effect = [resp_login, resp_fw, resp_system]
|
||||
@@ -209,10 +219,10 @@ class TestDotHillClient(test.TestCase):
|
||||
mock_requests_get.return_value = m
|
||||
ret = self.client._api_request('/path')
|
||||
self.assertTrue(type(ret) == etree.RestrictedElement)
|
||||
self.assertRaises(dh_exception.DotHillConnectionError,
|
||||
self.assertRaises(stx_exception.ConnectionError,
|
||||
self.client._api_request,
|
||||
'/path')
|
||||
self.assertRaises(dh_exception.DotHillConnectionError,
|
||||
self.assertRaises(stx_exception.ConnectionError,
|
||||
self.client._api_request,
|
||||
'/path')
|
||||
|
||||
@@ -222,22 +232,22 @@ class TestDotHillClient(test.TestCase):
|
||||
invalid_tree = etree.XML(invalid_xml)
|
||||
ret = self.client._assert_response_ok(ok_tree)
|
||||
self.assertIsNone(ret)
|
||||
self.assertRaises(dh_exception.DotHillRequestError,
|
||||
self.assertRaises(stx_exception.RequestError,
|
||||
self.client._assert_response_ok,
|
||||
not_ok_tree)
|
||||
self.assertRaises(dh_exception.DotHillRequestError,
|
||||
self.assertRaises(stx_exception.RequestError,
|
||||
self.client._assert_response_ok, invalid_tree)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_backend_exists(self, mock_request):
|
||||
mock_request.side_effect = [dh_exception.DotHillRequestError,
|
||||
mock_request.side_effect = [stx_exception.RequestError,
|
||||
fake_xml]
|
||||
self.assertFalse(self.client.backend_exists('backend_name',
|
||||
'linear'))
|
||||
self.assertTrue(self.client.backend_exists('backend_name',
|
||||
'linear'))
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_backend_stats(self, mock_request):
|
||||
stats = {'free_capacity_gb': 1979,
|
||||
'total_capacity_gb': 1979}
|
||||
@@ -250,7 +260,7 @@ class TestDotHillClient(test.TestCase):
|
||||
self.assertEqual(stats, self.client.backend_stats('A',
|
||||
'virtual'))
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_get_lun(self, mock_request):
|
||||
mock_request.side_effect = [etree.XML(response_no_lun),
|
||||
etree.XML(response_lun)]
|
||||
@@ -259,7 +269,7 @@ class TestDotHillClient(test.TestCase):
|
||||
ret = self.client._get_first_available_lun_for_host("fakehost")
|
||||
self.assertEqual(2, ret)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_get_ports(self, mock_request):
|
||||
mock_request.side_effect = [etree.XML(response_ports)]
|
||||
ret = self.client.get_active_target_ports()
|
||||
@@ -273,19 +283,19 @@ class TestDotHillClient(test.TestCase):
|
||||
'target-id': 'id5',
|
||||
'status': 'Up'}], ret)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_get_fc_ports(self, mock_request):
|
||||
mock_request.side_effect = [etree.XML(response_ports)]
|
||||
ret = self.client.get_active_fc_target_ports()
|
||||
self.assertEqual(['id2'], ret)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_get_iscsi_iqns(self, mock_request):
|
||||
mock_request.side_effect = [etree.XML(response_ports)]
|
||||
ret = self.client.get_active_iscsi_target_iqns()
|
||||
self.assertEqual(['id4', 'id5'], ret)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_get_iscsi_portals(self, mock_request):
|
||||
portals = {'10.0.0.12': 'Up', '10.0.0.11': 'Up'}
|
||||
mock_request.side_effect = [etree.XML(response_ports_linear),
|
||||
@@ -295,7 +305,7 @@ class TestDotHillClient(test.TestCase):
|
||||
ret = self.client.get_active_iscsi_target_portals()
|
||||
self.assertEqual(portals, ret)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_delete_snapshot(self, mock_request):
|
||||
mock_request.side_effect = [None, None]
|
||||
self.client.delete_snapshot('dummy', 'linear')
|
||||
@@ -303,7 +313,7 @@ class TestDotHillClient(test.TestCase):
|
||||
self.client.delete_snapshot('dummy', 'paged')
|
||||
mock_request.assert_called_with('/delete/snapshot', 'dummy')
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, '_request')
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_list_luns_for_host(self, mock_request):
|
||||
mock_request.side_effect = [etree.XML(response_no_lun),
|
||||
etree.XML(response_lun)]
|
||||
@@ -316,42 +326,44 @@ class TestDotHillClient(test.TestCase):
|
||||
|
||||
|
||||
class FakeConfiguration1(object):
|
||||
dothill_backend_name = 'OpenStack'
|
||||
dothill_backend_type = 'linear'
|
||||
seagate_pool_name = 'OpenStack'
|
||||
seagate_pool_type = 'linear'
|
||||
san_ip = '10.0.0.1'
|
||||
san_login = 'manage'
|
||||
san_password = '!manage'
|
||||
dothill_api_protocol = 'http'
|
||||
seagate_api_protocol = 'http'
|
||||
driver_use_ssl = True
|
||||
driver_ssl_cert_verify = False
|
||||
|
||||
def safe_get(self, key):
|
||||
return 'fakevalue'
|
||||
|
||||
|
||||
class FakeConfiguration2(FakeConfiguration1):
|
||||
dothill_iscsi_ips = ['10.0.0.11']
|
||||
seagate_iscsi_ips = ['10.0.0.11']
|
||||
use_chap_auth = None
|
||||
|
||||
|
||||
class TestFCDotHillCommon(test.TestCase):
|
||||
class TestFCSeagateCommon(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TestFCDotHillCommon, self).setUp()
|
||||
super(TestFCSeagateCommon, self).setUp()
|
||||
self.config = FakeConfiguration1()
|
||||
self.common = dothill_common.DotHillCommon(self.config)
|
||||
self.common = STXCommon(self.config)
|
||||
self.common.client_login = mock.MagicMock()
|
||||
self.common.client_logout = mock.MagicMock()
|
||||
self.common.serialNumber = "xxxxx"
|
||||
self.common.owner = "A"
|
||||
self.connector_element = "wwpns"
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'get_serial_number')
|
||||
@mock.patch.object(dothill.DotHillClient, 'get_owner_info')
|
||||
@mock.patch.object(dothill.DotHillClient, 'backend_exists')
|
||||
@mock.patch.object(STXClient, 'get_serial_number')
|
||||
@mock.patch.object(STXClient, 'get_owner_info')
|
||||
@mock.patch.object(STXClient, 'backend_exists')
|
||||
def test_do_setup(self, mock_backend_exists,
|
||||
mock_owner_info, mock_serial_number):
|
||||
mock_backend_exists.side_effect = [False, True]
|
||||
mock_owner_info.return_value = "A"
|
||||
mock_serial_number.return_value = "xxxxx"
|
||||
self.assertRaises(dh_exception.DotHillInvalidBackend,
|
||||
self.assertRaises(stx_exception.InvalidBackend,
|
||||
self.common.do_setup, None)
|
||||
self.assertIsNone(self.common.do_setup(None))
|
||||
mock_backend_exists.assert_called_with(self.common.backend_name,
|
||||
@@ -387,9 +399,9 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
connector,
|
||||
self.connector_element))
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'backend_stats')
|
||||
@mock.patch.object(STXClient, 'backend_stats')
|
||||
def test_update_volume_stats(self, mock_stats):
|
||||
mock_stats.side_effect = [dh_exception.DotHillRequestError,
|
||||
mock_stats.side_effect = [stx_exception.RequestError,
|
||||
stats_large_space]
|
||||
|
||||
self.assertRaises(exception.Invalid, self.common._update_volume_stats)
|
||||
@@ -403,16 +415,16 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
'multiattach': True,
|
||||
'free_capacity_gb': 90,
|
||||
'location_info':
|
||||
'DotHillVolumeDriver:xxxxx:OpenStack:A',
|
||||
'SeagateVolumeDriver:xxxxx:OpenStack:A',
|
||||
'pool_name': 'OpenStack',
|
||||
'total_capacity_gb': 100}],
|
||||
'storage_protocol': None,
|
||||
'vendor_name': 'DotHill',
|
||||
'vendor_name': 'Seagate',
|
||||
'volume_backend_name': None}, self.common.stats)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'create_volume')
|
||||
@mock.patch.object(STXClient, 'create_volume')
|
||||
def test_create_volume(self, mock_create):
|
||||
mock_create.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_create.side_effect = [stx_exception.RequestError, None]
|
||||
|
||||
self.assertRaises(exception.Invalid, self.common.create_volume,
|
||||
test_volume)
|
||||
@@ -423,12 +435,12 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
self.common.backend_name,
|
||||
self.common.backend_type)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'delete_volume')
|
||||
@mock.patch.object(STXClient, 'delete_volume')
|
||||
def test_delete_volume(self, mock_delete):
|
||||
not_found_e = dh_exception.DotHillRequestError(
|
||||
not_found_e = stx_exception.RequestError(
|
||||
'The volume was not found on this system.')
|
||||
mock_delete.side_effect = [not_found_e,
|
||||
dh_exception.DotHillRequestError,
|
||||
stx_exception.RequestError,
|
||||
None]
|
||||
self.assertIsNone(self.common.delete_volume(test_volume))
|
||||
self.assertRaises(exception.Invalid, self.common.delete_volume,
|
||||
@@ -436,19 +448,19 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
self.assertIsNone(self.common.delete_volume(test_volume))
|
||||
mock_delete.assert_called_with(encoded_volid)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'copy_volume')
|
||||
@mock.patch.object(dothill.DotHillClient, 'backend_stats')
|
||||
@mock.patch.object(STXClient, 'copy_volume')
|
||||
@mock.patch.object(STXClient, 'backend_stats')
|
||||
def test_create_cloned_volume(self, mock_stats, mock_copy):
|
||||
mock_stats.side_effect = [stats_low_space, stats_large_space,
|
||||
stats_large_space]
|
||||
|
||||
self.assertRaises(
|
||||
dh_exception.DotHillNotEnoughSpace,
|
||||
stx_exception.NotEnoughSpace,
|
||||
self.common.create_cloned_volume,
|
||||
dest_volume, detached_volume)
|
||||
self.assertFalse(mock_copy.called)
|
||||
|
||||
mock_copy.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_copy.side_effect = [stx_exception.RequestError, None]
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.common.create_cloned_volume,
|
||||
dest_volume, detached_volume)
|
||||
@@ -461,20 +473,20 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
self.common.backend_name,
|
||||
self.common.backend_type)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'copy_volume')
|
||||
@mock.patch.object(dothill.DotHillClient, 'backend_stats')
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'extend_volume')
|
||||
@mock.patch.object(STXClient, 'copy_volume')
|
||||
@mock.patch.object(STXClient, 'backend_stats')
|
||||
@mock.patch.object(STXCommon, 'extend_volume')
|
||||
def test_create_cloned_volume_larger(self, mock_extend, mock_stats,
|
||||
mock_copy):
|
||||
mock_stats.side_effect = [stats_low_space, stats_large_space,
|
||||
stats_large_space]
|
||||
|
||||
self.assertRaises(dh_exception.DotHillNotEnoughSpace,
|
||||
self.assertRaises(stx_exception.NotEnoughSpace,
|
||||
self.common.create_cloned_volume,
|
||||
dest_volume_larger, detached_volume)
|
||||
self.assertFalse(mock_copy.called)
|
||||
|
||||
mock_copy.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_copy.side_effect = [stx_exception.RequestError, None]
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.common.create_cloned_volume,
|
||||
dest_volume_larger, detached_volume)
|
||||
@@ -489,20 +501,20 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
mock_extend.assert_called_once_with(dest_volume_larger,
|
||||
dest_volume_larger['size'])
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'get_volume_size')
|
||||
@mock.patch.object(dothill.DotHillClient, 'extend_volume')
|
||||
@mock.patch.object(dothill.DotHillClient, 'copy_volume')
|
||||
@mock.patch.object(dothill.DotHillClient, 'backend_stats')
|
||||
@mock.patch.object(STXClient, 'get_volume_size')
|
||||
@mock.patch.object(STXClient, 'extend_volume')
|
||||
@mock.patch.object(STXClient, 'copy_volume')
|
||||
@mock.patch.object(STXClient, 'backend_stats')
|
||||
def test_create_volume_from_snapshot(self, mock_stats, mock_copy,
|
||||
mock_extend, mock_get_size):
|
||||
mock_stats.side_effect = [stats_low_space, stats_large_space,
|
||||
stats_large_space]
|
||||
|
||||
self.assertRaises(dh_exception.DotHillNotEnoughSpace,
|
||||
self.assertRaises(stx_exception.NotEnoughSpace,
|
||||
self.common.create_volume_from_snapshot,
|
||||
dest_volume, test_snap)
|
||||
|
||||
mock_copy.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_copy.side_effect = [stx_exception.RequestError, None]
|
||||
mock_get_size.return_value = test_snap['volume_size']
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.common.create_volume_from_snapshot,
|
||||
@@ -517,10 +529,10 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
self.common.backend_type)
|
||||
mock_extend.assert_called_with('vqqqqqqqqqqqqqqqqqqq', '10GiB')
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'get_volume_size')
|
||||
@mock.patch.object(dothill.DotHillClient, 'extend_volume')
|
||||
@mock.patch.object(STXClient, 'get_volume_size')
|
||||
@mock.patch.object(STXClient, 'extend_volume')
|
||||
def test_extend_volume(self, mock_extend, mock_size):
|
||||
mock_extend.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_extend.side_effect = [stx_exception.RequestError, None]
|
||||
mock_size.side_effect = [10, 10]
|
||||
self.assertRaises(exception.Invalid, self.common.extend_volume,
|
||||
test_volume, 20)
|
||||
@@ -528,9 +540,9 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
self.assertIsNone(ret)
|
||||
mock_extend.assert_called_with(encoded_volid, '10GiB')
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'create_snapshot')
|
||||
@mock.patch.object(STXClient, 'create_snapshot')
|
||||
def test_create_snapshot(self, mock_create):
|
||||
mock_create.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_create.side_effect = [stx_exception.RequestError, None]
|
||||
|
||||
self.assertRaises(exception.Invalid, self.common.create_snapshot,
|
||||
test_snap)
|
||||
@@ -538,12 +550,12 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
self.assertIsNone(ret)
|
||||
mock_create.assert_called_with(encoded_volid, 'sqqqqqqqqqqqqqqqqqqq')
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'delete_snapshot')
|
||||
@mock.patch.object(STXClient, 'delete_snapshot')
|
||||
def test_delete_snapshot(self, mock_delete):
|
||||
not_found_e = dh_exception.DotHillRequestError(
|
||||
not_found_e = stx_exception.RequestError(
|
||||
'The volume was not found on this system.')
|
||||
mock_delete.side_effect = [not_found_e,
|
||||
dh_exception.DotHillRequestError,
|
||||
stx_exception.RequestError,
|
||||
None]
|
||||
|
||||
self.assertIsNone(self.common.delete_snapshot(test_snap))
|
||||
@@ -553,9 +565,9 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
mock_delete.assert_called_with('sqqqqqqqqqqqqqqqqqqq',
|
||||
self.common.backend_type)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'map_volume')
|
||||
@mock.patch.object(STXClient, 'map_volume')
|
||||
def test_map_volume(self, mock_map):
|
||||
mock_map.side_effect = [dh_exception.DotHillRequestError, 10]
|
||||
mock_map.side_effect = [stx_exception.RequestError, 10]
|
||||
|
||||
self.assertRaises(exception.Invalid, self.common.map_volume,
|
||||
test_volume, connector, self.connector_element)
|
||||
@@ -565,9 +577,9 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
mock_map.assert_called_with(encoded_volid,
|
||||
connector, self.connector_element)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'unmap_volume')
|
||||
@mock.patch.object(STXClient, 'unmap_volume')
|
||||
def test_unmap_volume(self, mock_unmap):
|
||||
mock_unmap.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_unmap.side_effect = [stx_exception.RequestError, None]
|
||||
|
||||
self.assertRaises(exception.Invalid, self.common.unmap_volume,
|
||||
test_volume, connector, self.connector_element)
|
||||
@@ -577,11 +589,11 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
mock_unmap.assert_called_with(encoded_volid, connector,
|
||||
self.connector_element)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'copy_volume')
|
||||
@mock.patch.object(dothill.DotHillClient, 'delete_volume')
|
||||
@mock.patch.object(dothill.DotHillClient, 'modify_volume_name')
|
||||
@mock.patch.object(STXClient, 'copy_volume')
|
||||
@mock.patch.object(STXClient, 'delete_volume')
|
||||
@mock.patch.object(STXClient, 'modify_volume_name')
|
||||
def test_retype(self, mock_modify, mock_delete, mock_copy):
|
||||
mock_copy.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_copy.side_effect = [stx_exception.RequestError, None]
|
||||
self.assertRaises(exception.Invalid, self.common.migrate_volume,
|
||||
test_retype_volume, test_host)
|
||||
ret = self.common.migrate_volume(test_retype_volume, test_host)
|
||||
@@ -590,20 +602,20 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
{'capabilities': {}})
|
||||
self.assertEqual((False, None), ret)
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, '_get_vol_name')
|
||||
@mock.patch.object(dothill.DotHillClient, 'modify_volume_name')
|
||||
@mock.patch.object(STXCommon, '_get_vol_name')
|
||||
@mock.patch.object(STXClient, 'modify_volume_name')
|
||||
def test_manage_existing(self, mock_modify, mock_volume):
|
||||
existing_ref = {'source-name': 'xxxx'}
|
||||
mock_modify.side_effect = [dh_exception.DotHillRequestError, None]
|
||||
mock_modify.side_effect = [stx_exception.RequestError, None]
|
||||
self.assertRaises(exception.Invalid, self.common.manage_existing,
|
||||
test_volume, existing_ref)
|
||||
ret = self.common.manage_existing(test_volume, existing_ref)
|
||||
self.assertIsNone(ret)
|
||||
|
||||
@mock.patch.object(dothill.DotHillClient, 'get_volume_size')
|
||||
@mock.patch.object(STXClient, 'get_volume_size')
|
||||
def test_manage_existing_get_size(self, mock_volume):
|
||||
existing_ref = {'source-name': 'xxxx'}
|
||||
mock_volume.side_effect = [dh_exception.DotHillRequestError, 1]
|
||||
mock_volume.side_effect = [stx_exception.RequestError, 1]
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.common.manage_existing_get_size,
|
||||
None, existing_ref)
|
||||
@@ -611,28 +623,28 @@ class TestFCDotHillCommon(test.TestCase):
|
||||
self.assertEqual(1, ret)
|
||||
|
||||
|
||||
class TestISCSIDotHillCommon(TestFCDotHillCommon):
|
||||
class TestISCSISeagateCommon(TestFCSeagateCommon):
|
||||
def setUp(self):
|
||||
super(TestISCSIDotHillCommon, self).setUp()
|
||||
super(TestISCSISeagateCommon, self).setUp()
|
||||
self.connector_element = 'initiator'
|
||||
|
||||
|
||||
class TestDotHillFC(test.TestCase):
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'do_setup')
|
||||
class TestSeagateFC(test.TestCase):
|
||||
@mock.patch.object(STXCommon, 'do_setup')
|
||||
def setUp(self, mock_setup):
|
||||
super(TestDotHillFC, self).setUp()
|
||||
self.vendor_name = 'DotHill'
|
||||
super(TestSeagateFC, self).setUp()
|
||||
self.vendor_name = 'Seagate'
|
||||
|
||||
mock_setup.return_value = True
|
||||
|
||||
def fake_init(self, *args, **kwargs):
|
||||
super(dothill_fc.DotHillFCDriver, self).__init__()
|
||||
super(STXFCDriver, self).__init__()
|
||||
self.common = None
|
||||
self.configuration = FakeConfiguration1()
|
||||
self.lookup_service = fczm_utils.create_lookup_service()
|
||||
|
||||
dothill_fc.DotHillFCDriver.__init__ = fake_init
|
||||
self.driver = dothill_fc.DotHillFCDriver()
|
||||
STXFCDriver.__init__ = fake_init
|
||||
self.driver = STXFCDriver()
|
||||
self.driver.do_setup(None)
|
||||
|
||||
def _test_with_mock(self, mock, method, args, expected=None):
|
||||
@@ -641,42 +653,42 @@ class TestDotHillFC(test.TestCase):
|
||||
self.assertRaises(exception.Invalid, func, *args)
|
||||
self.assertEqual(expected, func(*args))
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'create_volume')
|
||||
@mock.patch.object(STXCommon, 'create_volume')
|
||||
def test_create_volume(self, mock_create):
|
||||
self._test_with_mock(mock_create, 'create_volume', [None])
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon,
|
||||
@mock.patch.object(STXCommon,
|
||||
'create_cloned_volume')
|
||||
def test_create_cloned_volume(self, mock_create):
|
||||
self._test_with_mock(mock_create, 'create_cloned_volume', [None, None])
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon,
|
||||
@mock.patch.object(STXCommon,
|
||||
'create_volume_from_snapshot')
|
||||
def test_create_volume_from_snapshot(self, mock_create):
|
||||
self._test_with_mock(mock_create, 'create_volume_from_snapshot',
|
||||
[None, None])
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'delete_volume')
|
||||
@mock.patch.object(STXCommon, 'delete_volume')
|
||||
def test_delete_volume(self, mock_delete):
|
||||
self._test_with_mock(mock_delete, 'delete_volume', [None])
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'create_snapshot')
|
||||
@mock.patch.object(STXCommon, 'create_snapshot')
|
||||
def test_create_snapshot(self, mock_create):
|
||||
self._test_with_mock(mock_create, 'create_snapshot', [None])
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'delete_snapshot')
|
||||
@mock.patch.object(STXCommon, 'delete_snapshot')
|
||||
def test_delete_snapshot(self, mock_delete):
|
||||
self._test_with_mock(mock_delete, 'delete_snapshot', [None])
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'extend_volume')
|
||||
@mock.patch.object(STXCommon, 'extend_volume')
|
||||
def test_extend_volume(self, mock_extend):
|
||||
self._test_with_mock(mock_extend, 'extend_volume', [None, 10])
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'client_logout')
|
||||
@mock.patch.object(dothill_common.DotHillCommon,
|
||||
@mock.patch.object(STXCommon, 'client_logout')
|
||||
@mock.patch.object(STXCommon,
|
||||
'get_active_fc_target_ports')
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'map_volume')
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'client_login')
|
||||
@mock.patch.object(STXCommon, 'map_volume')
|
||||
@mock.patch.object(STXCommon, 'client_login')
|
||||
def test_initialize_connection(self, mock_login, mock_map, mock_ports,
|
||||
mock_logout):
|
||||
mock_login.return_value = None
|
||||
@@ -698,8 +710,8 @@ class TestDotHillFC(test.TestCase):
|
||||
'target_lun': 1,
|
||||
'target_discovered': True}}, ret)
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'unmap_volume')
|
||||
@mock.patch.object(dothill.DotHillClient, 'list_luns_for_host')
|
||||
@mock.patch.object(STXCommon, 'unmap_volume')
|
||||
@mock.patch.object(STXClient, 'list_luns_for_host')
|
||||
def test_terminate_connection(self, mock_list, mock_unmap):
|
||||
mock_unmap.side_effect = [1]
|
||||
mock_list.side_effect = ['yes']
|
||||
@@ -710,7 +722,7 @@ class TestDotHillFC(test.TestCase):
|
||||
ret = self.driver.terminate_connection(test_volume, connector)
|
||||
self.assertEqual(actual, ret)
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'get_volume_stats')
|
||||
@mock.patch.object(STXCommon, 'get_volume_stats')
|
||||
def test_get_volume_stats(self, mock_stats):
|
||||
stats = {'storage_protocol': None,
|
||||
'driver_version': self.driver.VERSION,
|
||||
@@ -734,7 +746,7 @@ class TestDotHillFC(test.TestCase):
|
||||
self.assertEqual(stats, ret)
|
||||
mock_stats.assert_called_with(True)
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'retype')
|
||||
@mock.patch.object(STXCommon, 'retype')
|
||||
def test_retype(self, mock_retype):
|
||||
mock_retype.side_effect = [exception.Invalid, True, False]
|
||||
args = [None, None, None, None, None]
|
||||
@@ -742,12 +754,12 @@ class TestDotHillFC(test.TestCase):
|
||||
self.assertTrue(self.driver.retype(*args))
|
||||
self.assertFalse(self.driver.retype(*args))
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'manage_existing')
|
||||
@mock.patch.object(STXCommon, 'manage_existing')
|
||||
def test_manage_existing(self, mock_manage_existing):
|
||||
self._test_with_mock(mock_manage_existing, 'manage_existing',
|
||||
[None, None])
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon,
|
||||
@mock.patch.object(STXCommon,
|
||||
'manage_existing_get_size')
|
||||
def test_manage_size(self, mock_manage_size):
|
||||
mock_manage_size.side_effect = [exception.Invalid, 1]
|
||||
@@ -757,30 +769,30 @@ class TestDotHillFC(test.TestCase):
|
||||
self.assertEqual(1, self.driver.manage_existing_get_size(None, None))
|
||||
|
||||
|
||||
class TestDotHillISCSI(TestDotHillFC):
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'do_setup')
|
||||
class TestSeagateISCSI(TestSeagateFC):
|
||||
@mock.patch.object(STXCommon, 'do_setup')
|
||||
def setUp(self, mock_setup):
|
||||
super(TestDotHillISCSI, self).setUp()
|
||||
self.vendor_name = 'DotHill'
|
||||
super(TestSeagateISCSI, self).setUp()
|
||||
self.vendor_name = 'Seagate'
|
||||
mock_setup.return_value = True
|
||||
|
||||
def fake_init(self, *args, **kwargs):
|
||||
super(dothill_iscsi.DotHillISCSIDriver, self).__init__()
|
||||
super(STXISCSIDriver, self).__init__()
|
||||
self.common = None
|
||||
self.configuration = FakeConfiguration2()
|
||||
self.iscsi_ips = ['10.0.0.11']
|
||||
|
||||
dothill_iscsi.DotHillISCSIDriver.__init__ = fake_init
|
||||
self.driver = dothill_iscsi.DotHillISCSIDriver()
|
||||
STXISCSIDriver.__init__ = fake_init
|
||||
self.driver = STXISCSIDriver()
|
||||
self.driver.do_setup(None)
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'client_logout')
|
||||
@mock.patch.object(dothill_common.DotHillCommon,
|
||||
@mock.patch.object(STXCommon, 'client_logout')
|
||||
@mock.patch.object(STXCommon,
|
||||
'get_active_iscsi_target_portals')
|
||||
@mock.patch.object(dothill_common.DotHillCommon,
|
||||
@mock.patch.object(STXCommon,
|
||||
'get_active_iscsi_target_iqns')
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'map_volume')
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'client_login')
|
||||
@mock.patch.object(STXCommon, 'map_volume')
|
||||
@mock.patch.object(STXCommon, 'client_login')
|
||||
def test_initialize_connection(self, mock_login, mock_map, mock_iqns,
|
||||
mock_portals, mock_logout):
|
||||
mock_login.return_value = None
|
||||
@@ -803,7 +815,7 @@ class TestDotHillISCSI(TestDotHillFC):
|
||||
'target_discovered': True,
|
||||
'target_portal': '10.0.0.11:3260'}}, ret)
|
||||
|
||||
@mock.patch.object(dothill_common.DotHillCommon, 'unmap_volume')
|
||||
@mock.patch.object(STXCommon, 'unmap_volume')
|
||||
def test_terminate_connection(self, mock_unmap):
|
||||
mock_unmap.side_effect = [exception.Invalid, 1]
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from cinder.volume.drivers.dothill import dothill_client
|
||||
import cinder.volume.drivers.stx.client as client
|
||||
|
||||
|
||||
class LenovoClient(dothill_client.DotHillClient):
|
||||
class LenovoClient(client.STXClient):
|
||||
|
||||
def __init__(self, host, login, password, protocol, ssl_verify):
|
||||
super(LenovoClient, self).__init__(host, login, password, protocol,
|
||||
|
||||
@@ -17,25 +17,33 @@
|
||||
from oslo_config import cfg
|
||||
|
||||
from cinder.volume import configuration
|
||||
from cinder.volume.drivers.dothill import dothill_common
|
||||
from cinder.volume.drivers.lenovo import lenovo_client
|
||||
import cinder.volume.drivers.lenovo.lenovo_client as lenovo_client
|
||||
import cinder.volume.drivers.stx.common as common
|
||||
|
||||
common_opts = [
|
||||
cfg.StrOpt('lenovo_backend_name',
|
||||
cfg.StrOpt('lenovo_pool_name',
|
||||
deprecated_name='lenovo_backend_name',
|
||||
default='A',
|
||||
help="Pool or Vdisk name to use for volume creation."),
|
||||
cfg.StrOpt('lenovo_backend_type',
|
||||
cfg.StrOpt('lenovo_pool_type',
|
||||
deprecated_name='lenovo_backend_type',
|
||||
choices=['linear', 'virtual'],
|
||||
default='virtual',
|
||||
help="linear (for VDisk) or virtual (for Pool)."),
|
||||
cfg.StrOpt('lenovo_api_protocol',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='driver_use_ssl should be used instead.',
|
||||
choices=['http', 'https'],
|
||||
default='https',
|
||||
help="Lenovo api interface protocol."),
|
||||
cfg.BoolOpt('lenovo_verify_certificate',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use driver_ssl_cert_verify instead.',
|
||||
default=False,
|
||||
help="Whether to verify Lenovo array SSL certificate."),
|
||||
cfg.StrOpt('lenovo_verify_certificate_path',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use driver_ssl_cert_path instead.',
|
||||
help="Lenovo array SSL certificate path.")
|
||||
]
|
||||
|
||||
@@ -50,19 +58,26 @@ CONF.register_opts(common_opts, group=configuration.SHARED_CONF_GROUP)
|
||||
CONF.register_opts(iscsi_opts, group=configuration.SHARED_CONF_GROUP)
|
||||
|
||||
|
||||
class LenovoCommon(dothill_common.DotHillCommon):
|
||||
VERSION = "1.6"
|
||||
class LenovoCommon(common.STXCommon):
|
||||
VERSION = "2.0"
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.vendor_name = "Lenovo"
|
||||
self.backend_name = self.config.lenovo_backend_name
|
||||
self.backend_type = self.config.lenovo_backend_type
|
||||
self.backend_name = self.config.lenovo_pool_name
|
||||
self.backend_type = self.config.lenovo_pool_type
|
||||
self.api_protocol = self.config.lenovo_api_protocol
|
||||
ssl_verify = False
|
||||
# check for deprecated options...
|
||||
if (self.api_protocol == 'https' and
|
||||
self.config.lenovo_verify_certificate):
|
||||
ssl_verify = self.config.lenovo_verify_certificate_path or True
|
||||
# ...then check common options
|
||||
if self.config.driver_use_ssl:
|
||||
self.api_protocol = 'https'
|
||||
if self.config.driver_ssl_cert_verify:
|
||||
ssl_verify = self.config.driver_ssl_cert_path or True
|
||||
|
||||
self.client = lenovo_client.LenovoClient(self.config.san_ip,
|
||||
self.config.san_login,
|
||||
self.config.san_password,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 Dot Hill Systems Corp.
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-2019 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -16,12 +16,12 @@
|
||||
#
|
||||
|
||||
from cinder import interface
|
||||
from cinder.volume.drivers.dothill import dothill_fc
|
||||
from cinder.volume.drivers.lenovo import lenovo_common
|
||||
import cinder.volume.drivers.lenovo.lenovo_common as lenovo_common
|
||||
import cinder.volume.drivers.stx.fc as fc
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class LenovoFCDriver(dothill_fc.DotHillFCDriver):
|
||||
class LenovoFCDriver(fc.STXFCDriver):
|
||||
"""OpenStack Fibre Channel cinder drivers for Lenovo Storage arrays.
|
||||
|
||||
.. code-block:: default
|
||||
@@ -30,10 +30,10 @@ class LenovoFCDriver(dothill_fc.DotHillFCDriver):
|
||||
1.0 - Inheriting from DotHill cinder drivers.
|
||||
1.6 - Add management path redundancy and reduce load placed
|
||||
on management controller.
|
||||
|
||||
2.0 - DotHill driver renamed to Seagate (STX)
|
||||
"""
|
||||
|
||||
VERSION = "1.6"
|
||||
VERSION = "2.0"
|
||||
|
||||
SUPPORTED = True
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 Dot Hill Systems Corp.
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-2019 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -16,12 +16,12 @@
|
||||
#
|
||||
|
||||
from cinder import interface
|
||||
from cinder.volume.drivers.dothill import dothill_iscsi
|
||||
from cinder.volume.drivers.lenovo import lenovo_common
|
||||
import cinder.volume.drivers.lenovo.lenovo_common as lenovo_common
|
||||
import cinder.volume.drivers.stx.iscsi as iscsi
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class LenovoISCSIDriver(dothill_iscsi.DotHillISCSIDriver):
|
||||
class LenovoISCSIDriver(iscsi.STXISCSIDriver):
|
||||
"""OpenStack iSCSI cinder drivers for Lenovo Storage arrays.
|
||||
|
||||
.. code-block:: default
|
||||
@@ -30,9 +30,10 @@ class LenovoISCSIDriver(dothill_iscsi.DotHillISCSIDriver):
|
||||
1.0 - Inheriting from DotHill cinder drivers.
|
||||
1.6 - Add management path redundancy and reduce load placed
|
||||
on management controller.
|
||||
2.0 - DotHill driver renamed to Seagate (STX)
|
||||
"""
|
||||
|
||||
VERSION = "1.6"
|
||||
VERSION = "2.0"
|
||||
|
||||
SUPPORTED = True
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from cinder.volume.drivers.dothill import dothill_client
|
||||
import cinder.volume.drivers.stx.client as client
|
||||
|
||||
|
||||
class HPMSAClient(dothill_client.DotHillClient):
|
||||
class HPMSAClient(client.STXClient):
|
||||
|
||||
def __init__(self, host, login, password, protocol, ssl_verify):
|
||||
super(HPMSAClient, self).__init__(host, login, password,
|
||||
|
||||
@@ -17,27 +17,34 @@
|
||||
from oslo_config import cfg
|
||||
|
||||
from cinder.volume import configuration
|
||||
from cinder.volume.drivers.dothill import dothill_common
|
||||
from cinder.volume.drivers.san.hp import hpmsa_client
|
||||
import cinder.volume.drivers.san.hp.hpmsa_client as hpmsa_client
|
||||
import cinder.volume.drivers.stx.common as common
|
||||
|
||||
common_opts = [
|
||||
cfg.StrOpt('hpmsa_backend_name',
|
||||
cfg.StrOpt('hpmsa_pool_name',
|
||||
deprecated_name='hpmsa_backend_name',
|
||||
default='A',
|
||||
help="Pool or Vdisk name to use for volume creation."),
|
||||
cfg.StrOpt('hpmsa_backend_type',
|
||||
cfg.StrOpt('hpmsa_pool_type',
|
||||
deprecated_name='hpmsa_backend_type',
|
||||
choices=['linear', 'virtual'],
|
||||
default='virtual',
|
||||
help="linear (for Vdisk) or virtual (for Pool)."),
|
||||
cfg.StrOpt('hpmsa_api_protocol',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='driver_use_ssl should be used instead.',
|
||||
choices=['http', 'https'],
|
||||
default='https',
|
||||
help="HPMSA API interface protocol."),
|
||||
cfg.BoolOpt('hpmsa_verify_certificate',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use driver_ssl_cert_verify instead.',
|
||||
default=False,
|
||||
help="Whether to verify HPMSA array SSL certificate."),
|
||||
cfg.StrOpt('hpmsa_verify_certificate_path',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use driver_ssl_cert_path instead.',
|
||||
help="HPMSA array SSL certificate path."),
|
||||
|
||||
]
|
||||
|
||||
iscsi_opts = [
|
||||
@@ -51,19 +58,25 @@ CONF.register_opts(common_opts, group=configuration.SHARED_CONF_GROUP)
|
||||
CONF.register_opts(iscsi_opts, group=configuration.SHARED_CONF_GROUP)
|
||||
|
||||
|
||||
class HPMSACommon(dothill_common.DotHillCommon):
|
||||
VERSION = "1.6"
|
||||
class HPMSACommon(common.STXCommon):
|
||||
VERSION = "2.0"
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.vendor_name = "HPMSA"
|
||||
self.backend_name = self.config.hpmsa_backend_name
|
||||
self.backend_type = self.config.hpmsa_backend_type
|
||||
self.backend_name = self.config.hpmsa_pool_name
|
||||
self.backend_type = self.config.hpmsa_pool_type
|
||||
self.api_protocol = self.config.hpmsa_api_protocol
|
||||
ssl_verify = False
|
||||
# check deprecated vendor-specific options ...
|
||||
if (self.api_protocol == 'https' and
|
||||
self.config.hpmsa_verify_certificate):
|
||||
ssl_verify = self.config.hpmsa_verify_certificate_path or True
|
||||
# ... before newer common options
|
||||
if self.config.driver_use_ssl:
|
||||
self.api_protocol = 'https'
|
||||
if self.config.driver_ssl_cert_verify:
|
||||
ssl_verify = self.config.driver_ssl_cert_path or True
|
||||
|
||||
self.client = hpmsa_client.HPMSAClient(self.config.san_ip,
|
||||
self.config.san_login,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 Dot Hill Systems Corp.
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-2019 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -16,12 +16,12 @@
|
||||
#
|
||||
|
||||
from cinder import interface
|
||||
from cinder.volume.drivers.dothill import dothill_fc
|
||||
from cinder.volume.drivers.san.hp import hpmsa_common
|
||||
import cinder.volume.drivers.san.hp.hpmsa_common as hpmsa_common
|
||||
import cinder.volume.drivers.stx.fc as fc
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class HPMSAFCDriver(dothill_fc.DotHillFCDriver):
|
||||
class HPMSAFCDriver(fc.STXFCDriver):
|
||||
"""OpenStack Fibre Channel cinder drivers for HPMSA arrays.
|
||||
|
||||
.. code-block:: default
|
||||
@@ -30,12 +30,12 @@ class HPMSAFCDriver(dothill_fc.DotHillFCDriver):
|
||||
1.0 - Inheriting from DotHill cinder drivers.
|
||||
1.6 - Add management path redundancy and reduce load placed
|
||||
on management controller.
|
||||
|
||||
2.0 - DotHill driver renamed to Seagate (STX)
|
||||
"""
|
||||
|
||||
VERSION = "1.6"
|
||||
VERSION = "2.0"
|
||||
|
||||
CI_WIKI_NAME = "Vedams-HPMSA_FCISCSIDriver_CI"
|
||||
CI_WIKI_NAME = "HPMSA_CI"
|
||||
|
||||
SUPPORTED = True
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 Dot Hill Systems Corp.
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-2019 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -16,12 +16,12 @@
|
||||
#
|
||||
|
||||
from cinder import interface
|
||||
from cinder.volume.drivers.dothill import dothill_iscsi
|
||||
from cinder.volume.drivers.san.hp import hpmsa_common
|
||||
import cinder.volume.drivers.san.hp.hpmsa_common as hpmsa_common
|
||||
import cinder.volume.drivers.stx.iscsi as iscsi
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class HPMSAISCSIDriver(dothill_iscsi.DotHillISCSIDriver):
|
||||
class HPMSAISCSIDriver(iscsi.STXISCSIDriver):
|
||||
"""OpenStack iSCSI cinder drivers for HPMSA arrays.
|
||||
|
||||
.. code-block:: default
|
||||
@@ -30,12 +30,12 @@ class HPMSAISCSIDriver(dothill_iscsi.DotHillISCSIDriver):
|
||||
1.0 - Inheriting from DotHill cinder drivers.
|
||||
1.6 - Add management path redundancy and reduce load placed
|
||||
on management controller.
|
||||
|
||||
2.0 - DotHill driver renamed to Seagate (STX)
|
||||
"""
|
||||
|
||||
VERSION = "1.6"
|
||||
VERSION = "2.0"
|
||||
|
||||
CI_WIKI_NAME = "Vedams-HPMSA_FCISCSIDriver_CI"
|
||||
CI_WIKI_NAME = "HPMSA_CI"
|
||||
|
||||
SUPPORTED = True
|
||||
|
||||
|
||||
+33
-34
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 Dot Hill Systems Corp.
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-2019 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -19,22 +19,23 @@ import hashlib
|
||||
import math
|
||||
import time
|
||||
|
||||
from cinder import utils
|
||||
from defusedxml import lxml as etree
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import units
|
||||
import pprint
|
||||
import requests
|
||||
import six
|
||||
|
||||
from cinder import coordination
|
||||
from cinder.i18n import _
|
||||
from cinder.volume.drivers.dothill import exception as dh_exception
|
||||
import cinder.volume.drivers.stx.exception as stx_exception
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DotHillClient(object):
|
||||
@six.add_metaclass(utils.TraceWrapperMetaclass)
|
||||
class STXClient(object):
|
||||
def __init__(self, host, login, password, protocol, ssl_verify):
|
||||
self._mgmt_ip_addrs = list(map(str.strip, host.split(',')))
|
||||
self._login = login
|
||||
@@ -65,7 +66,7 @@ class DotHillClient(object):
|
||||
self._session_key = session_key
|
||||
except Exception as e:
|
||||
msg = _("Cannot parse session key: %s") % e.msg
|
||||
raise dh_exception.DotHillConnectionError(message=msg)
|
||||
raise stx_exception.ConnectionError(message=msg)
|
||||
|
||||
def login(self):
|
||||
if self._session_key is None:
|
||||
@@ -86,7 +87,7 @@ class DotHillClient(object):
|
||||
LOG.debug("Logged in to array %s at %s (session %s)",
|
||||
self._array_name, self._base_url, self._session_key)
|
||||
return
|
||||
except dh_exception.DotHillConnectionError:
|
||||
except stx_exception.ConnectionError:
|
||||
not_responding = self._curr_ip_addr
|
||||
LOG.exception('session_login failed to connect to %s',
|
||||
self._curr_ip_addr)
|
||||
@@ -99,11 +100,11 @@ class DotHillClient(object):
|
||||
try:
|
||||
self._get_session_key()
|
||||
return
|
||||
except dh_exception.DotHillConnectionError:
|
||||
except stx_exception.ConnectionError:
|
||||
LOG.error('Failed to connect to %s',
|
||||
self._curr_ip_addr)
|
||||
continue
|
||||
raise dh_exception.DotHillConnectionError(
|
||||
raise stx_exception.ConnectionError(
|
||||
message=_("Failed to log in to management controller"))
|
||||
|
||||
@coordination.synchronized('{self._driver_name}-{self._array_name}')
|
||||
@@ -114,7 +115,7 @@ class DotHillClient(object):
|
||||
hash_ = "%s_%s" % (self._login, self._password)
|
||||
if six.PY3:
|
||||
hash_ = hash_.encode('utf-8')
|
||||
hash_ = hashlib.md5(hash_)
|
||||
hash_ = hashlib.md5(hash_) # nosec
|
||||
digest = hash_.hexdigest()
|
||||
|
||||
url = self._base_url + "/login/" + digest
|
||||
@@ -123,24 +124,24 @@ class DotHillClient(object):
|
||||
except requests.exceptions.RequestException:
|
||||
msg = _("Failed to obtain MC session key")
|
||||
LOG.exception(msg)
|
||||
raise dh_exception.DotHillConnectionError(message=msg)
|
||||
raise stx_exception.ConnectionError(message=msg)
|
||||
|
||||
self._get_auth_token(xml.text.encode('utf8'))
|
||||
LOG.debug("session key = %s", self._session_key)
|
||||
if self._session_key is None:
|
||||
raise dh_exception.DotHillAuthenticationError
|
||||
raise stx_exception.AuthenticationError
|
||||
|
||||
def _assert_response_ok(self, tree):
|
||||
"""Parses the XML returned by the device to check the return code.
|
||||
|
||||
Raises a DotHillRequestError error if the return code is not 0
|
||||
Raises a RequestError error if the return code is not 0
|
||||
or if the return code is None.
|
||||
"""
|
||||
# Get the return code for the operation, raising an exception
|
||||
# if it is not present.
|
||||
return_code = tree.findtext(".//PROPERTY[@name='return-code']")
|
||||
if not return_code:
|
||||
raise dh_exception.DotHillRequestError(message="No status found")
|
||||
raise stx_exception.RequestError(message="No status found")
|
||||
|
||||
# If no error occurred, just return.
|
||||
if return_code == '0':
|
||||
@@ -150,7 +151,7 @@ class DotHillClient(object):
|
||||
msg = "%s (%s)" % (tree.findtext(".//PROPERTY[@name='response']"),
|
||||
return_code)
|
||||
|
||||
raise dh_exception.DotHillRequestError(message=msg)
|
||||
raise stx_exception.RequestError(message=msg)
|
||||
|
||||
def _build_request_url(self, path, *args, **kargs):
|
||||
url = self._base_url + path
|
||||
@@ -165,10 +166,10 @@ class DotHillClient(object):
|
||||
def _request(self, path, *args, **kargs):
|
||||
"""Performs an API request on the array, with retry.
|
||||
|
||||
Propagates a DotHillConnectionError if no valid response is
|
||||
Propagates a ConnectionError if no valid response is
|
||||
received from the array, e.g. if the network is down.
|
||||
|
||||
Propagates a DotHillRequestError if the device returned a response
|
||||
Propagates a RequestError if the device returned a response
|
||||
but the status is not 0. The device error message will be used
|
||||
in the exception message.
|
||||
|
||||
@@ -178,14 +179,14 @@ class DotHillClient(object):
|
||||
while tries_left > 0:
|
||||
try:
|
||||
return self._api_request(path, *args, **kargs)
|
||||
except dh_exception.DotHillConnectionError as e:
|
||||
except stx_exception.ConnectionError as e:
|
||||
if tries_left < 1:
|
||||
LOG.error("Array Connection error: "
|
||||
"%s (no more retries)", e.msg)
|
||||
raise
|
||||
# Retry on any network connection errors, SSL errors, etc
|
||||
LOG.error("Array Connection error: %s (retrying)", e.msg)
|
||||
except dh_exception.DotHillRequestError as e:
|
||||
except stx_exception.RequestError as e:
|
||||
if tries_left < 1:
|
||||
LOG.error("Array Request error: %s (no more retries)",
|
||||
e.msg)
|
||||
@@ -204,7 +205,7 @@ class DotHillClient(object):
|
||||
def _api_request(self, path, *args, **kargs):
|
||||
"""Performs an HTTP request on the device, with locking.
|
||||
|
||||
Raises a DotHillRequestError if the device returned but the status is
|
||||
Raises a RequestError if the device returned but the status is
|
||||
not 0. The device error message will be used in the exception message.
|
||||
|
||||
If the status is OK, returns the XML data for further processing.
|
||||
@@ -221,7 +222,7 @@ class DotHillClient(object):
|
||||
except Exception as e:
|
||||
message = _("Exception handling URL %(url)s: %(msg)s") % {
|
||||
'url': url, 'msg': e}
|
||||
raise dh_exception.DotHillConnectionError(message=message)
|
||||
raise stx_exception.ConnectionError(message=message)
|
||||
|
||||
if path == "/show/volumecopy-status":
|
||||
return tree
|
||||
@@ -253,7 +254,7 @@ class DotHillClient(object):
|
||||
|
||||
try:
|
||||
self._request("/create/volume", name, **path_dict)
|
||||
except dh_exception.DotHillRequestError as e:
|
||||
except stx_exception.RequestError as e:
|
||||
# -10186 => The specified name is already in use.
|
||||
# This can occur during controller failover.
|
||||
if '(-10186)' in e.msg:
|
||||
@@ -266,7 +267,7 @@ class DotHillClient(object):
|
||||
def delete_volume(self, name):
|
||||
try:
|
||||
self._request("/delete/volumes", name)
|
||||
except dh_exception.DotHillRequestError as e:
|
||||
except stx_exception.RequestError as e:
|
||||
# -10075 => The specified volume was not found.
|
||||
# This can occur during controller failover.
|
||||
if '(-10075)' in e.msg:
|
||||
@@ -282,7 +283,7 @@ class DotHillClient(object):
|
||||
def create_snapshot(self, volume_name, snap_name):
|
||||
try:
|
||||
self._request("/create/snapshots", snap_name, volumes=volume_name)
|
||||
except dh_exception.DotHillRequestError as e:
|
||||
except stx_exception.RequestError as e:
|
||||
# -10186 => The specified name is already in use.
|
||||
# This can occur during controller failover.
|
||||
if '(-10186)' in e.msg:
|
||||
@@ -296,7 +297,7 @@ class DotHillClient(object):
|
||||
self._request("/delete/snapshot", "cleanup", snap_name)
|
||||
else:
|
||||
self._request("/delete/snapshot", snap_name)
|
||||
except dh_exception.DotHillRequestError as e:
|
||||
except stx_exception.RequestError as e:
|
||||
# -10050 => The volume was not found on this system.
|
||||
# This can occur during controller failover.
|
||||
if '(-10050)' in e.msg:
|
||||
@@ -312,7 +313,7 @@ class DotHillClient(object):
|
||||
path = "/show/pools"
|
||||
self._request(path, backend_name)
|
||||
return True
|
||||
except dh_exception.DotHillRequestError:
|
||||
except stx_exception.RequestError:
|
||||
return False
|
||||
|
||||
def _get_size(self, size):
|
||||
@@ -378,7 +379,7 @@ class DotHillClient(object):
|
||||
firsthost, lun)
|
||||
return lun
|
||||
lun += 1
|
||||
raise dh_exception.DotHillRequestError(
|
||||
raise stx_exception.RequestError(
|
||||
message=_("No LUNs available for mapping to host %s.") % host)
|
||||
|
||||
def _is_mapped(self, volume_name, ids):
|
||||
@@ -393,7 +394,7 @@ class DotHillClient(object):
|
||||
if iid in ids:
|
||||
LOG.debug("volume '{}' is already mapped to {} at lun {}".
|
||||
format(volume_name, iid, lun))
|
||||
return lun
|
||||
return int(lun)
|
||||
except Exception as e:
|
||||
LOG.exception("failed to look up mappings for volume '%s'",
|
||||
volume_name)
|
||||
@@ -403,8 +404,6 @@ class DotHillClient(object):
|
||||
@coordination.synchronized('{self._driver_name}-{self._array_name}-map')
|
||||
def map_volume(self, volume_name, connector, connector_element):
|
||||
# If multiattach enabled, its possible the volume is already mapped
|
||||
LOG.debug("map_volume(%s, %s, %s)", volume_name,
|
||||
pprint.pformat(connector), connector_element)
|
||||
lun = self._is_mapped(volume_name, connector[connector_element])
|
||||
if lun:
|
||||
return lun
|
||||
@@ -418,7 +417,7 @@ class DotHillClient(object):
|
||||
hostname = self._safe_hostname(connector['host'])
|
||||
try:
|
||||
self._request("/create/host", hostname, id=host)
|
||||
except dh_exception.DotHillRequestError as e:
|
||||
except stx_exception.RequestError as e:
|
||||
# -10058: The host identifier or nickname is already in use
|
||||
if '(-10058)' in e.msg:
|
||||
LOG.error("While trying to create host nickname"
|
||||
@@ -437,7 +436,7 @@ class DotHillClient(object):
|
||||
host=host,
|
||||
access="rw")
|
||||
return lun
|
||||
except dh_exception.DotHillRequestError as e:
|
||||
except stx_exception.RequestError as e:
|
||||
# -3177 => "The specified LUN overlaps a previously defined LUN
|
||||
if '(-3177)' in e.msg:
|
||||
LOG.info("Unable to map volume"
|
||||
@@ -456,7 +455,7 @@ class DotHillClient(object):
|
||||
e)
|
||||
raise
|
||||
|
||||
raise dh_exception.DotHillRequestError(
|
||||
raise stx_exception.RequestError(
|
||||
message=_("Failed to find a free LUN for host %s") % host)
|
||||
|
||||
def unmap_volume(self, volume_name, connector, connector_element):
|
||||
@@ -466,7 +465,7 @@ class DotHillClient(object):
|
||||
host = connector['initiator']
|
||||
try:
|
||||
self._request("/unmap/volume", volume_name, host=host)
|
||||
except dh_exception.DotHillRequestError as e:
|
||||
except stx_exception.RequestError as e:
|
||||
# -10050 => The volume was not found on this system.
|
||||
# This can occur during controller failover.
|
||||
if '(-10050)' in e.msg:
|
||||
@@ -522,7 +521,7 @@ class DotHillClient(object):
|
||||
else:
|
||||
if count >= 5:
|
||||
LOG.error('Error in copying volume: %s', src_name)
|
||||
raise dh_exception.DotHillRequestError
|
||||
raise stx_exception.RequestError
|
||||
|
||||
time.sleep(1)
|
||||
count += 1
|
||||
+75
-55
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 Dot Hill Systems Corp.
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-2019 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -15,7 +15,7 @@
|
||||
# under the License.
|
||||
#
|
||||
"""
|
||||
Volume driver common utilities for DotHill Storage array
|
||||
Volume driver common utilities for Seagate storage arrays
|
||||
"""
|
||||
|
||||
import base64
|
||||
@@ -28,34 +28,56 @@ from oslo_log import log as logging
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.objects import fields
|
||||
from cinder.volume.drivers.dothill import dothill_client as dothill
|
||||
from cinder.volume.drivers.dothill import exception as dh_exception
|
||||
from cinder import utils
|
||||
from cinder.volume import configuration
|
||||
import cinder.volume.drivers.stx.client as client
|
||||
import cinder.volume.drivers.stx.exception as stx_exception
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
common_opts = [
|
||||
cfg.StrOpt('seagate_pool_name',
|
||||
default='A',
|
||||
help="Pool or vdisk name to use for volume creation."),
|
||||
cfg.StrOpt('seagate_pool_type',
|
||||
choices=['linear', 'virtual'],
|
||||
default='virtual',
|
||||
help="linear (for vdisk) or virtual (for virtual pool)."),
|
||||
]
|
||||
|
||||
iscsi_opts = [
|
||||
cfg.ListOpt('seagate_iscsi_ips',
|
||||
default=[],
|
||||
help="List of comma-separated target iSCSI IP addresses."),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(common_opts, group=configuration.SHARED_CONF_GROUP)
|
||||
CONF.register_opts(iscsi_opts, group=configuration.SHARED_CONF_GROUP)
|
||||
|
||||
|
||||
class DotHillCommon(object):
|
||||
VERSION = "1.6"
|
||||
@six.add_metaclass(utils.TraceWrapperMetaclass)
|
||||
class STXCommon(object):
|
||||
VERSION = "2.0"
|
||||
|
||||
stats = {}
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.vendor_name = "DotHill"
|
||||
self.backend_name = self.config.dothill_backend_name
|
||||
self.backend_type = self.config.dothill_backend_type
|
||||
self.api_protocol = self.config.dothill_api_protocol
|
||||
ssl_verify = False
|
||||
if (self.api_protocol == 'https' and
|
||||
self.config.dothill_verify_certificate):
|
||||
ssl_verify = self.config.dothill_verify_certificate_path or True
|
||||
self.client = dothill.DotHillClient(self.config.san_ip,
|
||||
self.config.san_login,
|
||||
self.config.san_password,
|
||||
self.api_protocol,
|
||||
ssl_verify)
|
||||
self.vendor_name = "Seagate"
|
||||
self.backend_name = self.config.seagate_pool_name
|
||||
self.backend_type = self.config.seagate_pool_type
|
||||
self.api_protocol = 'http'
|
||||
if self.config.driver_use_ssl:
|
||||
self.api_protocol = 'https'
|
||||
ssl_verify = self.config.driver_ssl_cert_verify
|
||||
if ssl_verify and self.config.driver_ssl_cert_path:
|
||||
ssl_verify = self.config.driver_ssl_cert_path
|
||||
self.client = client.STXClient(self.config.san_ip,
|
||||
self.config.san_login,
|
||||
self.config.san_password,
|
||||
self.api_protocol,
|
||||
ssl_verify)
|
||||
|
||||
def get_version(self):
|
||||
return self.VERSION
|
||||
@@ -70,18 +92,18 @@ class DotHillCommon(object):
|
||||
def client_login(self):
|
||||
try:
|
||||
self.client.login()
|
||||
except dh_exception.DotHillConnectionError as ex:
|
||||
except stx_exception.ConnectionError as ex:
|
||||
msg = _("Failed to connect to %(vendor_name)s Array %(host)s: "
|
||||
"%(err)s") % {'vendor_name': self.vendor_name,
|
||||
'host': self.config.san_ip,
|
||||
'err': six.text_type(ex)}
|
||||
LOG.error(msg)
|
||||
raise dh_exception.DotHillConnectionError(message=msg)
|
||||
except dh_exception.DotHillAuthenticationError:
|
||||
raise stx_exception.ConnectionError(message=msg)
|
||||
except stx_exception.AuthenticationError:
|
||||
msg = _("Failed to log on %s Array "
|
||||
"(invalid login?).") % self.vendor_name
|
||||
LOG.error(msg)
|
||||
raise dh_exception.DotHillAuthenticationError(message=msg)
|
||||
raise stx_exception.AuthenticationError(message=msg)
|
||||
|
||||
def _get_serial_number(self):
|
||||
self.serialNumber = self.client.get_serial_number()
|
||||
@@ -94,7 +116,7 @@ class DotHillCommon(object):
|
||||
if not self.client.backend_exists(self.backend_name,
|
||||
self.backend_type):
|
||||
self.client_logout()
|
||||
raise dh_exception.DotHillInvalidBackend(backend=self.backend_name)
|
||||
raise stx_exception.InvalidBackend(backend=self.backend_name)
|
||||
|
||||
def client_logout(self):
|
||||
self.client.logout()
|
||||
@@ -108,7 +130,7 @@ class DotHillCommon(object):
|
||||
return "s%s" % snapshot_name
|
||||
|
||||
def _encode_name(self, name):
|
||||
"""Get converted DotHill volume name.
|
||||
"""Get converted array volume name.
|
||||
|
||||
Converts the openstack volume id from
|
||||
fceec30e-98bc-4ce5-85ff-d7309cc17cc2
|
||||
@@ -135,7 +157,7 @@ class DotHillCommon(object):
|
||||
|
||||
def create_volume(self, volume):
|
||||
self.client_login()
|
||||
# Use base64 to encode the volume name (UUID is too long for DotHill)
|
||||
# Use base64 to encode the volume name (UUID is too long)
|
||||
volume_name = self._get_vol_name(volume['id'])
|
||||
volume_size = "%dGiB" % volume['size']
|
||||
LOG.debug("Create Volume having display_name: %(display_name)s "
|
||||
@@ -149,7 +171,7 @@ class DotHillCommon(object):
|
||||
volume_size,
|
||||
self.backend_name,
|
||||
self.backend_type)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Creation of volume %s failed.", volume['id'])
|
||||
raise exception.Invalid(ex)
|
||||
|
||||
@@ -157,22 +179,19 @@ class DotHillCommon(object):
|
||||
self.client_logout()
|
||||
|
||||
def _assert_enough_space_for_copy(self, volume_size):
|
||||
"""The DotHill creates a snap pool before trying to copy the volume.
|
||||
"""The array creates a snap pool before trying to copy the volume.
|
||||
|
||||
The pool is 5.27GB or 20% of the volume size, whichever is larger.
|
||||
Verify that we have enough space for the pool and then copy
|
||||
Verify that we have enough space for the pool and then copy.
|
||||
"""
|
||||
pool_size = max(volume_size * 0.2, 5.27)
|
||||
required_size = pool_size + volume_size
|
||||
|
||||
if required_size > self.stats['pools'][0]['free_capacity_gb']:
|
||||
raise dh_exception.DotHillNotEnoughSpace(backend=self.backend_name)
|
||||
raise stx_exception.NotEnoughSpace(backend=self.backend_name)
|
||||
|
||||
def _assert_source_detached(self, volume):
|
||||
"""The DotHill requires a volume to be dettached to clone it.
|
||||
|
||||
Make sure that the volume is not in use when trying to copy it.
|
||||
"""
|
||||
"""The array requires volume to be detached before cloning."""
|
||||
if (volume['status'] != "available" or
|
||||
volume['attach_status'] == fields.VolumeAttachStatus.ATTACHED):
|
||||
LOG.error("Volume must be detached for clone operation.")
|
||||
@@ -196,7 +215,7 @@ class DotHillCommon(object):
|
||||
try:
|
||||
self.client.copy_volume(orig_name, dest_name,
|
||||
self.backend_name, self.backend_type)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Cloning of volume %s failed.",
|
||||
src_vref['id'])
|
||||
raise exception.Invalid(ex)
|
||||
@@ -219,7 +238,7 @@ class DotHillCommon(object):
|
||||
try:
|
||||
self.client.copy_volume(orig_name, dest_name,
|
||||
self.backend_name, self.backend_type)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Create volume failed from snapshot: %s",
|
||||
snapshot['id'])
|
||||
raise exception.Invalid(ex)
|
||||
@@ -239,7 +258,7 @@ class DotHillCommon(object):
|
||||
self.client_login()
|
||||
try:
|
||||
self.client.delete_volume(volume_name)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
# if the volume wasn't found, ignore the error
|
||||
if 'The volume was not found on this system.' in ex.args:
|
||||
return
|
||||
@@ -278,7 +297,7 @@ class DotHillCommon(object):
|
||||
self.backend_name,
|
||||
self.owner))
|
||||
pool['pool_name'] = self.backend_name
|
||||
except dh_exception.DotHillRequestError:
|
||||
except stx_exception.RequestError:
|
||||
err = (_("Unable to get stats for backend_name: %s") %
|
||||
self.backend_name)
|
||||
LOG.exception(err)
|
||||
@@ -304,7 +323,7 @@ class DotHillCommon(object):
|
||||
connector,
|
||||
connector_element)
|
||||
return data
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error mapping volume: %s", volume_name)
|
||||
raise exception.Invalid(ex)
|
||||
|
||||
@@ -320,7 +339,7 @@ class DotHillCommon(object):
|
||||
self.client.unmap_volume(volume_name,
|
||||
connector,
|
||||
connector_element)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error unmapping volume: %s", volume_name)
|
||||
raise exception.Invalid(ex)
|
||||
finally:
|
||||
@@ -329,21 +348,21 @@ class DotHillCommon(object):
|
||||
def get_active_fc_target_ports(self):
|
||||
try:
|
||||
return self.client.get_active_fc_target_ports()
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error getting active FC target ports.")
|
||||
raise exception.Invalid(ex)
|
||||
|
||||
def get_active_iscsi_target_iqns(self):
|
||||
try:
|
||||
return self.client.get_active_iscsi_target_iqns()
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error getting active ISCSI target iqns.")
|
||||
raise exception.Invalid(ex)
|
||||
|
||||
def get_active_iscsi_target_portals(self):
|
||||
try:
|
||||
return self.client.get_active_iscsi_target_portals()
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error getting active ISCSI target portals.")
|
||||
raise exception.Invalid(ex)
|
||||
|
||||
@@ -360,7 +379,7 @@ class DotHillCommon(object):
|
||||
self.client_login()
|
||||
try:
|
||||
self.client.create_snapshot(vol_name, snap_name)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Creation of snapshot failed for volume: %s",
|
||||
snapshot['volume_id'])
|
||||
raise exception.Invalid(ex)
|
||||
@@ -374,7 +393,7 @@ class DotHillCommon(object):
|
||||
self.client_login()
|
||||
try:
|
||||
self.client.delete_snapshot(snap_name, self.backend_type)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
# if the volume wasn't found, ignore the error
|
||||
if 'The volume was not found on this system.' in ex.args:
|
||||
return
|
||||
@@ -401,7 +420,7 @@ class DotHillCommon(object):
|
||||
self.client_login()
|
||||
try:
|
||||
self.client.extend_volume(volume_name, "%dGiB" % growth_size)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Extension of volume %s failed.", volume['id'])
|
||||
raise exception.Invalid(ex)
|
||||
finally:
|
||||
@@ -410,14 +429,14 @@ class DotHillCommon(object):
|
||||
def get_chap_record(self, initiator_name):
|
||||
try:
|
||||
return self.client.get_chap_record(initiator_name)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error getting chap record.")
|
||||
raise exception.Invalid(ex)
|
||||
|
||||
def create_chap_record(self, initiator_name, chap_secret):
|
||||
try:
|
||||
self.client.create_chap_record(initiator_name, chap_secret)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error creating chap record.")
|
||||
raise exception.Invalid(ex)
|
||||
|
||||
@@ -444,7 +463,8 @@ class DotHillCommon(object):
|
||||
except ValueError:
|
||||
return false_ret
|
||||
|
||||
if not (dest_type == 'DotHillVolumeDriver' and
|
||||
reqd_dest_type = '%sVolumeDriver' % self.vendor_name
|
||||
if not (dest_type == reqd_dest_type and
|
||||
dest_id == self.serialNumber and
|
||||
dest_owner == self.owner):
|
||||
return false_ret
|
||||
@@ -452,7 +472,7 @@ class DotHillCommon(object):
|
||||
source_name = self._get_vol_name(volume['name_id'])
|
||||
else:
|
||||
source_name = self._get_vol_name(volume['id'])
|
||||
# DotHill Array does not support duplicate names
|
||||
# the array does not support duplicate names
|
||||
dest_name = "m%s" % source_name[1:]
|
||||
|
||||
self.client_login()
|
||||
@@ -462,7 +482,7 @@ class DotHillCommon(object):
|
||||
self.client.delete_volume(source_name)
|
||||
self.client.modify_volume_name(dest_name, source_name)
|
||||
return (True, None)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error migrating volume: %s", source_name)
|
||||
raise exception.Invalid(ex)
|
||||
finally:
|
||||
@@ -473,10 +493,10 @@ class DotHillCommon(object):
|
||||
return ret[0]
|
||||
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
"""Manage an existing non-openstack DotHill volume
|
||||
"""Manage an existing non-openstack array volume
|
||||
|
||||
existing_ref is a dictionary of the form:
|
||||
{'source-name': <name of the existing DotHill volume>}
|
||||
{'source-name': <name of the existing volume>}
|
||||
"""
|
||||
target_vol_name = existing_ref['source-name']
|
||||
modify_target_vol_name = self._get_vol_name(volume['id'])
|
||||
@@ -485,7 +505,7 @@ class DotHillCommon(object):
|
||||
try:
|
||||
self.client.modify_volume_name(target_vol_name,
|
||||
modify_target_vol_name)
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error manage existing volume.")
|
||||
raise exception.Invalid(ex)
|
||||
finally:
|
||||
@@ -503,7 +523,7 @@ class DotHillCommon(object):
|
||||
try:
|
||||
size = self.client.get_volume_size(target_vol_name)
|
||||
return size
|
||||
except dh_exception.DotHillRequestError as ex:
|
||||
except stx_exception.RequestError as ex:
|
||||
LOG.exception("Error manage existing get volume size.")
|
||||
raise exception.Invalid(ex)
|
||||
finally:
|
||||
+6
-10
@@ -15,29 +15,25 @@ from cinder import exception
|
||||
from cinder.i18n import _
|
||||
|
||||
|
||||
class DotHillInvalidBackend(exception.VolumeDriverException):
|
||||
class InvalidBackend(exception.VolumeDriverException):
|
||||
message = _("Backend doesn't exist (%(backend)s)")
|
||||
|
||||
|
||||
class DotHillConnectionError(exception.VolumeDriverException):
|
||||
class ConnectionError(exception.VolumeDriverException):
|
||||
message = "%(message)s"
|
||||
|
||||
|
||||
class DotHillAuthenticationError(exception.VolumeDriverException):
|
||||
class AuthenticationError(exception.VolumeDriverException):
|
||||
message = "%(message)s"
|
||||
|
||||
|
||||
class DotHillNotEnoughSpace(exception.VolumeDriverException):
|
||||
class NotEnoughSpace(exception.VolumeDriverException):
|
||||
message = _("Not enough space on backend (%(backend)s)")
|
||||
|
||||
|
||||
class DotHillRequestError(exception.VolumeDriverException):
|
||||
class RequestError(exception.VolumeDriverException):
|
||||
message = "%(message)s"
|
||||
|
||||
|
||||
class DotHillNotTargetPortal(exception.VolumeDriverException):
|
||||
class NotTargetPortal(exception.VolumeDriverException):
|
||||
message = _("No active iSCSI portals with supplied iSCSI IPs")
|
||||
|
||||
|
||||
class DotHillDriverNotSupported(exception.VolumeDriverException):
|
||||
message = _("The Dot Hill driver is no longer supported.")
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 Dot Hill Systems Corp.
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-2019 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -16,17 +16,13 @@
|
||||
#
|
||||
|
||||
import cinder.volume.driver
|
||||
from cinder.volume.drivers.dothill import dothill_common
|
||||
from cinder.volume.drivers.dothill import exception as dh_exception
|
||||
from cinder.volume.drivers.san import san
|
||||
import cinder.volume.drivers.san.san as san
|
||||
import cinder.volume.drivers.stx.common as common
|
||||
from cinder.zonemanager import utils as fczm_utils
|
||||
|
||||
|
||||
# As of Pike, the DotHill driver is no longer considered supported,
|
||||
# but the code remains as it is still subclassed by other drivers.
|
||||
# The __init__() function prevents any direct instantiation.
|
||||
class DotHillFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
"""OpenStack Fibre Channel cinder drivers for DotHill Arrays.
|
||||
class STXFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
"""OpenStack Fibre Channel cinder drivers for Seagate arrays.
|
||||
|
||||
.. code:: text
|
||||
|
||||
@@ -44,20 +40,24 @@ class DotHillFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
1.6 - Add management path redundancy and reduce load placed
|
||||
on management controller.
|
||||
1.7 - Modified so it can't be invoked except as a superclass
|
||||
|
||||
2.0 - Reworked to create a new Seagate (STX) array driver.
|
||||
"""
|
||||
|
||||
VERSION = "2.0"
|
||||
|
||||
CI_WIKI_NAME = 'Seagate_CI'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Make sure we're not invoked directly
|
||||
if type(self) == DotHillFCDriver:
|
||||
raise dh_exception.DotHillDriverNotSupported
|
||||
super(DotHillFCDriver, self).__init__(*args, **kwargs)
|
||||
super(STXFCDriver, self).__init__(*args, **kwargs)
|
||||
self.common = None
|
||||
self.configuration.append_config_values(san.san_opts)
|
||||
self.lookup_service = fczm_utils.create_lookup_service()
|
||||
if type(self) != STXFCDriver:
|
||||
return
|
||||
self.configuration.append_config_values(common.common_opts)
|
||||
|
||||
def _init_common(self):
|
||||
return dothill_common.DotHillCommon(self.configuration)
|
||||
return common.STXCommon(self.configuration)
|
||||
|
||||
def _check_flags(self):
|
||||
required_flags = ['san_ip', 'san_login', 'san_password']
|
||||
@@ -105,12 +105,15 @@ class DotHillFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
info = {'driver_volume_type': 'fibre_channel', 'data': {}}
|
||||
try:
|
||||
self.common.unmap_volume(volume, connector, 'wwpns')
|
||||
if not self.common.client.list_luns_for_host(
|
||||
connector['wwpns'][0]):
|
||||
ports, init_targ_map = self.get_init_targ_map(connector)
|
||||
info['data'] = {'target_wwn': ports,
|
||||
'initiator_target_map': init_targ_map}
|
||||
# multiattach volumes cannot be unmapped here, but will
|
||||
# be implicity unmapped when the volume is deleted.
|
||||
if not volume.get('multiattach'):
|
||||
self.common.unmap_volume(volume, connector, 'wwpns')
|
||||
fczm_utils.remove_fc_zone(info)
|
||||
finally:
|
||||
return info
|
||||
+23
-18
@@ -1,6 +1,6 @@
|
||||
# Copyright 2014 Objectif Libre
|
||||
# Copyright 2015 Dot Hill Systems Corp.
|
||||
# Copyright 2016 Seagate Technology or one of its affiliates
|
||||
# Copyright 2016-2019 Seagate Technology or one of its affiliates
|
||||
#
|
||||
# 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
|
||||
@@ -20,20 +20,17 @@ from oslo_log import log as logging
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
import cinder.volume.driver
|
||||
from cinder.volume.drivers.dothill import dothill_common as dothillcommon
|
||||
from cinder.volume.drivers.dothill import exception as dh_exception
|
||||
from cinder.volume.drivers.san import san
|
||||
import cinder.volume.drivers.san.san as san
|
||||
import cinder.volume.drivers.stx.common as common
|
||||
import cinder.volume.drivers.stx.exception as stx_exception
|
||||
|
||||
|
||||
DEFAULT_ISCSI_PORT = "3260"
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# As of Pike, the DotHill driver is no longer considered supported,
|
||||
# but the code remains as it is still subclassed by other drivers.
|
||||
# The __init__() function prevents any direct instantiation.
|
||||
class DotHillISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
"""OpenStack iSCSI cinder drivers for DotHill Arrays.
|
||||
class STXISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
"""OpenStack iSCSI Cinder driver for Seagate storage arrays.
|
||||
|
||||
.. code:: text
|
||||
|
||||
@@ -53,20 +50,25 @@ class DotHillISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
1.6 - Add management path redundancy and reduce load placed
|
||||
on management controller.
|
||||
1.7 - Modified so it can't be invoked except as a superclass
|
||||
|
||||
2.0 - Reworked to create a new Seagate (STX) array driver.
|
||||
"""
|
||||
|
||||
VERSION = "2.0"
|
||||
|
||||
CI_WIKI_NAME = 'Seagate_CI'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Make sure we're not invoked directly
|
||||
if type(self) == DotHillISCSIDriver:
|
||||
raise dh_exception.DotHillDriverNotSupported
|
||||
super(DotHillISCSIDriver, self).__init__(*args, **kwargs)
|
||||
super(STXISCSIDriver, self).__init__(*args, **kwargs)
|
||||
self.common = None
|
||||
self.configuration.append_config_values(san.san_opts)
|
||||
self.iscsi_ips = None
|
||||
if type(self) != STXISCSIDriver:
|
||||
return
|
||||
self.configuration.append_config_values(common.common_opts)
|
||||
self.configuration.append_config_values(common.iscsi_opts)
|
||||
self.iscsi_ips = self.configuration.seagate_iscsi_ips
|
||||
|
||||
def _init_common(self):
|
||||
return dothillcommon.DotHillCommon(self.configuration)
|
||||
return common.STXCommon(self.configuration)
|
||||
|
||||
def _check_flags(self):
|
||||
required_flags = ['san_ip', 'san_login', 'san_password']
|
||||
@@ -130,7 +132,7 @@ class DotHillISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
break
|
||||
|
||||
if 'target_portal' not in data:
|
||||
raise dh_exception.DotHillNotTargetPortal()
|
||||
raise stx_exception.NotTargetPortal()
|
||||
|
||||
if self.configuration.use_chap_auth:
|
||||
chap_secret = self.common.get_chap_record(
|
||||
@@ -152,7 +154,10 @@ class DotHillISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
if type(connector) == dict and 'initiator' in connector:
|
||||
self.common.unmap_volume(volume, connector, 'initiator')
|
||||
# multiattach volumes cannot be unmapped here, but will
|
||||
# be implicity unmapped when the volume is deleted.
|
||||
if not volume.get('multiattach'):
|
||||
self.common.unmap_volume(volume, connector, 'initiator')
|
||||
|
||||
def get_volume_stats(self, refresh=False):
|
||||
stats = self.common.get_volume_stats(refresh)
|
||||
@@ -38,9 +38,10 @@ Supported operations
|
||||
Configuring the array
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Verify that the array can be managed via an HTTPS connection. HTTP can also
|
||||
be used if ``hpmsa_api_protocol=http`` is placed into the appropriate
|
||||
sections of the ``cinder.conf`` file.
|
||||
#. Verify that the array can be managed using an HTTPS connection. HTTP
|
||||
can also be used if ``hpmsa_api_protocol=http`` is placed into the
|
||||
appropriate sections of the ``cinder.conf`` file, but this option is
|
||||
deprecated and will be removed in a future release.
|
||||
|
||||
Confirm that virtual pools A and B are present if you plan to use virtual
|
||||
pools for OpenStack storage.
|
||||
@@ -50,12 +51,12 @@ Configuring the array
|
||||
creating or setting aside one disk group for each of the A and B
|
||||
controllers.
|
||||
|
||||
#. Edit the ``cinder.conf`` file to define a storage back end entry for each
|
||||
#. Edit the ``cinder.conf`` file to define a storage back-end entry for each
|
||||
storage pool on the array that will be managed by OpenStack. Each entry
|
||||
consists of a unique section name, surrounded by square brackets, followed
|
||||
by options specified in a ``key=value`` format.
|
||||
by options specified in ``key=value`` format.
|
||||
|
||||
* The ``hpmsa_backend_name`` value specifies the name of the storage pool
|
||||
* The ``hpmsa_pool_name`` value specifies the name of the storage pool
|
||||
or vdisk on the array.
|
||||
|
||||
* The ``volume_backend_name`` option value can be a unique value, if you
|
||||
@@ -64,92 +65,101 @@ Configuring the array
|
||||
volume scheduler choose where new volumes are allocated.
|
||||
|
||||
* The rest of the options will be repeated for each storage pool in a
|
||||
given array: ``volume_driver`` specifies the Cinder driver name;
|
||||
``san_ip`` specifies the IP addresses or host names of the array's
|
||||
management controllers; ``san_login`` and ``san_password`` specify
|
||||
the username and password of an array user account with ``manage``
|
||||
privileges; and ``hpmsa_iscsi_ips`` specfies the iSCSI IP addresses
|
||||
for the array if using the iSCSI transport protocol.
|
||||
given array:
|
||||
|
||||
* ``volume_driver`` specifies the Cinder driver name.
|
||||
* ``san_ip`` specifies the IP addresses or host names of the array's
|
||||
management controllers.
|
||||
* ``san_login`` and ``san_password`` specify the username and password
|
||||
of an array user account with ``manage`` privileges.
|
||||
* ``driver_use_ssl`` should be set to ``true`` to enable use of the
|
||||
HTTPS protocol.
|
||||
* ``hpmsa_iscsi_ips`` specfies the iSCSI IP addresses for the array
|
||||
if using the iSCSI transport protocol.
|
||||
|
||||
In the examples below, two back ends are defined, one for pool A and one for
|
||||
pool B, and a common ``volume_backend_name`` is used so that a single
|
||||
volume type definition can be used to allocate volumes from both pools.
|
||||
|
||||
**iSCSI example back-end entries**
|
||||
**Example: iSCSI example back-end entries**
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pool-a]
|
||||
hpmsa_backend_name = A
|
||||
hpmsa_pool_name = A
|
||||
volume_backend_name = hpmsa-array
|
||||
volume_driver = cinder.volume.drivers.san.hp.hpmsa_iscsi.HPMSAISCSIDriver
|
||||
san_ip = 10.1.2.3,10.1.2.4
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
hpmsa_iscsi_ips = 10.2.3.4,10.2.3.5
|
||||
driver_use_ssl = true
|
||||
|
||||
[pool-b]
|
||||
hpmsa_backend_name = B
|
||||
hpmsa_pool_name = B
|
||||
volume_backend_name = hpmsa-array
|
||||
volume_driver = cinder.volume.drivers.san.hp.hpmsa_iscsi.HPMSAISCSIDriver
|
||||
san_ip = 10.1.2.3,10.1.2.4
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
hpmsa_iscsi_ips = 10.2.3.4,10.2.3.5
|
||||
driver_use_ssl = true
|
||||
|
||||
**Fibre Channel example back-end entries**
|
||||
**Example: Fibre Channel example back-end entries**
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pool-a]
|
||||
hpmsa_backend_name = A
|
||||
hpmsa_pool_name = A
|
||||
volume_backend_name = hpmsa-array
|
||||
volume_driver = cinder.volume.drivers.san.hp.hpmsa_fc.HPMSAFCDriver
|
||||
san_ip = 10.1.2.3,10.1.2.4
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
driver_use_ssl = true
|
||||
|
||||
[pool-b]
|
||||
hpmsa_backend_name = B
|
||||
hpmsa_pool_name = B
|
||||
volume_backend_name = hpmsa-array
|
||||
volume_driver = cinder.volume.drivers.san.hp.hpmsa_fc.HPMSAFCDriver
|
||||
san_ip = 10.1.2.3,10.1.2.4
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
driver_use_ssl = true
|
||||
|
||||
#. If any ``volume_backend_name`` value refers to a vdisk rather than a
|
||||
virtual pool, add an additional statement ``hpmsa_backend_type = linear``
|
||||
virtual pool, add an additional statement ``hpmsa_pool_type = linear``
|
||||
to that back end entry.
|
||||
|
||||
#. If HTTPS is not enabled in the array, include ``hpmsa_api_protocol = http``
|
||||
in each of the back-end definitions.
|
||||
|
||||
#. If HTTPS is enabled, you can enable certificate verification with the option
|
||||
``hpmsa_verify_certificate=True``. You may also use the
|
||||
``hpmsa_verify_certificate_path`` parameter to specify the path to a
|
||||
CA\_BUNDLE file containing CAs other than those in the default list.
|
||||
#. If HTTPS is enabled, you can enable certificate verification with the
|
||||
option ``driver_ssl_cert_verify = True``. You may also use the
|
||||
``driver_ssl_cert_path`` option to specify the path to a
|
||||
CA_BUNDLE file containing CAs other than those in the default list.
|
||||
|
||||
#. Modify the ``[DEFAULT]`` section of the ``cinder.conf`` file to add an
|
||||
``enabled_back-ends`` parameter specifying the backend entries you added,
|
||||
``enabled_backends`` parameter specifying the back-end entries you added,
|
||||
and a ``default_volume_type`` parameter specifying the name of a volume type
|
||||
that you will create in the next step.
|
||||
|
||||
**Example of [DEFAULT] section changes**
|
||||
**Example: [DEFAULT] section changes**
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
# ...
|
||||
enabled_backends = pool-a,pool-b
|
||||
default_volume_type = hpmsa
|
||||
|
||||
|
||||
#. Create a new volume type for each distinct ``volume_backend_name`` value
|
||||
that you added in the ``cinder.conf`` file. The example below assumes that
|
||||
that you added to the ``cinder.conf`` file. The example below assumes that
|
||||
the same ``volume_backend_name=hpmsa-array`` option was specified in all
|
||||
of the entries, and specifies that the volume type ``hpmsa`` can be used to
|
||||
allocate volumes from any of them.
|
||||
|
||||
**Example of creating a volume type**
|
||||
**Example: Creating a volume type**
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
|
||||
@@ -44,9 +44,10 @@ Supported operations
|
||||
Configuring the array
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Verify that the array can be managed using an HTTPS connection. HTTP can
|
||||
also be used if ``lenovo_api_protocol=http`` is placed into the
|
||||
appropriate sections of the ``cinder.conf`` file.
|
||||
#. Verify that the array can be managed using an HTTPS connection. HTTP
|
||||
can also be used if ``hpmsa_api_protocol=http`` is placed into the
|
||||
appropriate sections of the ``cinder.conf`` file, but this option is
|
||||
deprecated and will be removed in a future release.
|
||||
|
||||
Confirm that virtual pools A and B are present if you plan to use
|
||||
virtual pools for OpenStack storage.
|
||||
@@ -56,21 +57,26 @@ Configuring the array
|
||||
entry consists of a unique section name, surrounded by square brackets,
|
||||
followed by options specified in ``key=value`` format.
|
||||
|
||||
- The ``lenovo_backend_name`` value specifies the name of the storage
|
||||
- The ``lenovo_pool_name`` value specifies the name of the storage
|
||||
pool on the array.
|
||||
|
||||
- The ``volume_backend_name`` option value can be a unique value, if
|
||||
you wish to be able to assign volumes to a specific storage pool on
|
||||
the array, or a name that's shared among multiple storage pools to
|
||||
the array, or a name that is shared among multiple storage pools to
|
||||
let the volume scheduler choose where new volumes are allocated.
|
||||
|
||||
- The rest of the options will be repeated for each storage pool in a
|
||||
given array: ``volume_driver`` specifies the Cinder driver name;
|
||||
``san_ip`` specifies the IP addresses or host names of the array's
|
||||
management controllers; ``san_login`` and ``san_password`` specify
|
||||
the username and password of an array user account with ``manage``
|
||||
privileges; and ``lenovo_iscsi_ips`` specfies the iSCSI IP
|
||||
addresses for the array if using the iSCSI transport protocol.
|
||||
given array:
|
||||
|
||||
* ``volume_driver`` specifies the Cinder driver name.
|
||||
* ``san_ip`` specifies the IP addresses or host names of the array's
|
||||
management controllers.
|
||||
* ``san_login`` and ``san_password`` specify the username and password
|
||||
of an array user account with ``manage`` privileges.
|
||||
* ``driver_use_ssl`` should be set to ``true`` to enable use of the
|
||||
HTTPS protocol.
|
||||
* ``lenovo_iscsi_ips`` specfies the iSCSI IP addresses for the array
|
||||
if using the iSCSI transport protocol.
|
||||
|
||||
In the examples below, two back ends are defined, one for pool A and one
|
||||
for pool B, and a common ``volume_backend_name`` is used so that a
|
||||
@@ -82,49 +88,53 @@ Configuring the array
|
||||
.. code-block:: ini
|
||||
|
||||
[pool-a]
|
||||
lenovo_backend_name = A
|
||||
lenovo_pool_name = A
|
||||
volume_backend_name = lenovo-array
|
||||
volume_driver = cinder.volume.drivers.lenovo.lenovo_iscsi.LenovoISCSIDriver
|
||||
san_ip = 10.1.2.3
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
lenovo_iscsi_ips = 10.2.3.4,10.2.3.5
|
||||
driver_use_ssl = true
|
||||
|
||||
[pool-b]
|
||||
lenovo_backend_name = B
|
||||
lenovo_pool_name = B
|
||||
volume_backend_name = lenovo-array
|
||||
volume_driver = cinder.volume.drivers.lenovo.lenovo_iscsi.LenovoISCSIDriver
|
||||
san_ip = 10.1.2.3
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
lenovo_iscsi_ips = 10.2.3.4,10.2.3.5
|
||||
driver_use_ssl = true
|
||||
|
||||
**Example: Fibre Channel example back-end entries**
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pool-a]
|
||||
lenovo_backend_name = A
|
||||
lenovo_pool_name = A
|
||||
volume_backend_name = lenovo-array
|
||||
volume_driver = cinder.volume.drivers.lenovo.lenovo_fc.LenovoFCDriver
|
||||
san_ip = 10.1.2.3
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
driver_use_ssl = true
|
||||
|
||||
[pool-b]
|
||||
lenovo_backend_name = B
|
||||
lenovo_pool_name = B
|
||||
volume_backend_name = lenovo-array
|
||||
volume_driver = cinder.volume.drivers.lenovo.lenovo_fc.LenovoFCDriver
|
||||
san_ip = 10.1.2.3
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
driver_use_ssl = true
|
||||
|
||||
#. If HTTPS is not enabled in the array, include
|
||||
#. If HTTPS is not enabled in the array, add
|
||||
``lenovo_api_protocol = http`` in each of the back-end definitions.
|
||||
|
||||
#. If HTTPS is enabled, you can enable certificate verification with the
|
||||
option ``lenovo_verify_certificate=True``. You may also use the
|
||||
``lenovo_verify_certificate_path`` parameter to specify the path to a
|
||||
option ``driver_ssl_cert_verify = True``. You may also use the
|
||||
``driver_ssl_cert_path`` option to specify the path to a
|
||||
CA_BUNDLE file containing CAs other than those in the default list.
|
||||
|
||||
#. Modify the ``[DEFAULT]`` section of the ``cinder.conf`` file to add an
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
=============================================
|
||||
Seagate Array Fibre Channel and iSCSI drivers
|
||||
=============================================
|
||||
|
||||
The ``STXFCDriver`` and ``STXISCSIDriver`` Cinder drivers allow the
|
||||
Seagate Technology (STX) storage arrays to be used for Block Storage in
|
||||
OpenStack deployments.
|
||||
|
||||
System requirements
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To use the Seagate drivers, the following are required:
|
||||
|
||||
- Seagate storage array with:
|
||||
|
||||
- iSCSI or FC host interfaces
|
||||
- G28x firmware or later
|
||||
|
||||
- Network connectivity between the OpenStack host and the array management
|
||||
interfaces
|
||||
|
||||
- The HTTPS or HTTP protocol must be enabled on the array
|
||||
|
||||
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.
|
||||
- Migrate a volume with back-end assistance.
|
||||
- Retype a volume.
|
||||
- Manage and unmanage a volume.
|
||||
|
||||
Configuring the array
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Verify that the array can be managed via an HTTPS connection. HTTP can also
|
||||
be used if ``driver_use_ssl`` is set to (or defaults to) False
|
||||
in the ``cinder.conf`` file.
|
||||
|
||||
Confirm that virtual pools A and B are present if you plan to use virtual
|
||||
pools for OpenStack storage.
|
||||
|
||||
If you plan to use vdisks instead of virtual pools, create or identify one
|
||||
or more vdisks to be used for OpenStack storage; typically this will mean
|
||||
creating or setting aside one disk group for each of the A and B
|
||||
controllers.
|
||||
|
||||
#. Edit the ``cinder.conf`` file to define a storage back-end entry for each
|
||||
storage pool on the array that will be managed by OpenStack. Each entry
|
||||
consists of a unique section name, surrounded by square brackets, followed
|
||||
by options specified in a ``key=value`` format.
|
||||
|
||||
* The ``seagate_pool_name`` value specifies the name of the storage pool
|
||||
or vdisk on the array.
|
||||
|
||||
* The ``volume_backend_name`` option value can be a unique value, if you
|
||||
wish to be able to assign volumes to a specific storage pool on the
|
||||
array, or a name that is shared among multiple storage pools to let the
|
||||
volume scheduler choose where new volumes are allocated.
|
||||
|
||||
#. The following ``cinder.conf`` options generally have identical values
|
||||
for each backend section on the array:
|
||||
|
||||
* ``volume_driver`` specifies the Cinder driver name.
|
||||
|
||||
* ``san_ip`` specifies the IP addresses or host names of the array's
|
||||
management controllers.
|
||||
|
||||
* ``san_login`` and ``san_password`` specify the username and password
|
||||
of an array user account with ``manage`` privileges
|
||||
|
||||
* ``driver_use_ssl`` must be set to True to enable use of the HTTPS
|
||||
protocol.
|
||||
|
||||
* ``seagate_iscsi_ips`` specfies the iSCSI IP addresses
|
||||
for the array if using the iSCSI transport protocol
|
||||
|
||||
In the examples below, two back ends are defined, one for pool A and one for
|
||||
pool B, and a common ``volume_backend_name`` is used so that a single
|
||||
volume type definition can be used to allocate volumes from both pools.
|
||||
|
||||
**iSCSI example back-end entries**
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pool-a]
|
||||
seagate_pool_name = A
|
||||
volume_backend_name = seagate-array
|
||||
volume_driver = cinder.volume.drivers.stx.iscsi.STXISCSIDriver
|
||||
san_ip = 10.1.2.3,10.1.2.4
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
seagate_iscsi_ips = 10.2.3.4,10.2.3.5
|
||||
driver_use_ssl = true
|
||||
|
||||
[pool-b]
|
||||
seagate_backend_name = B
|
||||
volume_backend_name = seagate-array
|
||||
volume_driver = cinder.volume.drivers.stx.iscsi.STXISCSIDriver
|
||||
san_ip = 10.1.2.3,10.1.2.4
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
seagate_iscsi_ips = 10.2.3.4,10.2.3.5
|
||||
driver_use_ssl = true
|
||||
|
||||
**Fibre Channel example back-end entries**
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pool-a]
|
||||
seagate_backend_name = A
|
||||
volume_backend_name = seagate-array
|
||||
volume_driver = cinder.volume.drivers.stx.fc.STXFCDriver
|
||||
san_ip = 10.1.2.3,10.1.2.4
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
driver_use_ssl = true
|
||||
|
||||
[pool-b]
|
||||
seagate_backend_name = B
|
||||
volume_backend_name = seagate-array
|
||||
volume_driver = cinder.volume.drivers.stx.fc.STXFCDriver
|
||||
san_ip = 10.1.2.3,10.1.2.4
|
||||
san_login = manage
|
||||
san_password = !manage
|
||||
driver_use_ssl = true
|
||||
|
||||
#. If any ``volume_backend_name`` value refers to a vdisk rather than a
|
||||
virtual pool, add an additional statement ``seagate_backend_type = linear``
|
||||
to that back-end entry.
|
||||
|
||||
#. If HTTPS is enabled, you can enable certificate verification with the option
|
||||
``driver_ssl_cert_verify = True``. You may also use the
|
||||
``driver_ssl_cert_path`` parameter to specify the path to a
|
||||
CA\_BUNDLE file containing CAs other than those in the default list.
|
||||
|
||||
#. Modify the ``[DEFAULT]`` section of the ``cinder.conf`` file to add an
|
||||
``enabled_backends`` parameter specifying the backend entries you added,
|
||||
and a ``default_volume_type`` parameter specifying the name of a volume type
|
||||
that you will create in the next step.
|
||||
|
||||
**Example of [DEFAULT] section changes**
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_backends = pool-a,pool-b
|
||||
default_volume_type = seagate
|
||||
|
||||
|
||||
#. Create a new volume type for each distinct ``volume_backend_name`` value
|
||||
that you added in the ``cinder.conf`` file. The example below assumes that
|
||||
the same ``volume_backend_name=seagate-array`` option was specified in all
|
||||
of the entries, and specifies that the volume type ``seagate`` can be used
|
||||
to allocate volumes from any of them.
|
||||
|
||||
**Example of creating a volume type**
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack volume type create seagate
|
||||
$ openstack volume type set --property volume_backend_name=seagate-array seagate
|
||||
|
||||
#. After modifying the ``cinder.conf`` file, restart the ``cinder-volume``
|
||||
service.
|
||||
|
||||
Driver-specific options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following table contains the configuration options that are specific to
|
||||
the Seagate drivers.
|
||||
|
||||
.. config-table::
|
||||
:config-target: Seagate
|
||||
|
||||
cinder.volume.drivers.stx.common
|
||||
@@ -162,6 +162,9 @@ title=Quobyte Storage Driver (quobyte)
|
||||
[driver.rbd]
|
||||
title=RBD (Ceph) Storage Driver (RBD)
|
||||
|
||||
[driver.seagate]
|
||||
title=Seagate Driver (iSCSI, FC)
|
||||
|
||||
[driver.sheepdog]
|
||||
title=Sheepdog Storage Driver (sheepdog)
|
||||
|
||||
@@ -251,6 +254,7 @@ driver.pure=complete
|
||||
driver.qnap=complete
|
||||
driver.quobyte=complete
|
||||
driver.rbd=complete
|
||||
driver.seagate=complete
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=complete
|
||||
@@ -316,6 +320,7 @@ driver.pure=complete
|
||||
driver.qnap=complete
|
||||
driver.quobyte=complete
|
||||
driver.rbd=complete
|
||||
driver.seagate=complete
|
||||
driver.sheepdog=complete
|
||||
driver.storpool=complete
|
||||
driver.synology=complete
|
||||
@@ -381,6 +386,7 @@ driver.pure=missing
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=missing
|
||||
driver.seagate=missing
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
@@ -449,6 +455,7 @@ driver.pure=missing
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=missing
|
||||
driver.seagate=missing
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
@@ -516,6 +523,7 @@ driver.pure=complete
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=complete
|
||||
driver.seagate=missing
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=complete
|
||||
driver.synology=missing
|
||||
@@ -584,6 +592,7 @@ driver.pure=complete
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=missing
|
||||
driver.seagate=missing
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
@@ -651,6 +660,7 @@ driver.pure=complete
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=complete
|
||||
driver.seagate=missing
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
@@ -719,6 +729,7 @@ driver.pure=missing
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=missing
|
||||
driver.seagate=missing
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=complete
|
||||
driver.synology=missing
|
||||
@@ -787,6 +798,7 @@ driver.pure=complete
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=complete
|
||||
driver.seagate=complete
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
@@ -852,6 +864,7 @@ driver.pure=missing
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=missing
|
||||
driver.seagate=missing
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
@@ -921,6 +934,7 @@ driver.pure=missing
|
||||
driver.qnap=missing
|
||||
driver.quobyte=missing
|
||||
driver.rbd=complete
|
||||
driver.seagate=missing
|
||||
driver.sheepdog=missing
|
||||
driver.storpool=missing
|
||||
driver.synology=missing
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The HPE MSA driver options ``hpmsa_backend_name`` and ``hpmsa_backend_type``
|
||||
options were deprecated in favor of ``hpmsa_pool_name`` and
|
||||
``hpmsa_pool_type`` to avoid confusion, and the
|
||||
``hpmsa_api_protocol``, ``hpmsa_verify_certificate``, and
|
||||
``hpmsa_verify_certificate_path`` options were deprecated in favor of
|
||||
the standard ``driver_use_ssl``, ``driver_ssl_cert_verify``, and
|
||||
``driver_ssl_cert_path`` options. To retain the default behavior, add
|
||||
``driver_use_ssl = true`` to back-end entries in ``cinder.conf``
|
||||
before the deprecated options are removed in a future release.
|
||||
|
||||
deprecations:
|
||||
- |
|
||||
The HPE MSA driver options ``hpmsa_backend_name`` and ``hpmsa_backend_type``
|
||||
options were deprecated in favor of ``hpmsa_pool_name`` and
|
||||
``hpmsa_pool_type`` to avoid confusion, and the
|
||||
``hpmsa_api_protocol``, ``hpmsa_verify_certificate``, and
|
||||
``hpmsa_verify_certificate_path`` options were deprecated in favor of
|
||||
the standard ``driver_use_ssl``, ``driver_ssl_cert_verify``, and
|
||||
``driver_ssl_cert_path`` options. To retain the default behavior, add
|
||||
``driver_use_ssl = true`` to back-end entries in ``cinder.conf``
|
||||
before the deprecated options are removed in a future release.
|
||||
|
||||
fixes:
|
||||
- |
|
||||
Fixed HPE MSA driver issue where a multi-attached volume could be
|
||||
unmapped while still in use.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The Lenovo driver options ``lenovo_backend_name`` and ``lenovo_backend_type``
|
||||
options were deprecated in favor of ``lenovo_pool_name`` and
|
||||
``lenovo_pool_type`` to avoid confusion, and the
|
||||
``lenovo_api_protocol``, ``lenovo_verify_certificate``, and
|
||||
``lenovo_verify_certificate_path`` options were deprecated in favor of
|
||||
the standard ``driver_use_ssl``, ``driver_ssl_cert_verify``, and
|
||||
``driver_ssl_cert_path`` options. To retain the default behavior, add
|
||||
``driver_use_ssl = true`` to back-end entries in ``cinder.conf``
|
||||
before the deprecated options are removed in a future release.
|
||||
|
||||
deprecations:
|
||||
- |
|
||||
The Lenovo driver options ``lenovo_backend_name`` and ``lenovo_backend_type``
|
||||
options were deprecated in favor of ``lenovo_pool_name`` and
|
||||
``lenovo_pool_type`` to avoid confusion, and the
|
||||
``lenovo_api_protocol``, ``lenovo_verify_certificate``, and
|
||||
``lenovo_verify_certificate_path`` options were deprecated in favor of
|
||||
the standard ``driver_use_ssl``, ``driver_ssl_cert_verify``, and
|
||||
``driver_ssl_cert_path`` options. To retain the default behavior, add
|
||||
``driver_use_ssl = true`` to back-end entries in ``cinder.conf``
|
||||
before the deprecated options are removed in a future release.
|
||||
|
||||
fixes:
|
||||
- |
|
||||
Fixed Lenovo driver issue where a multi-attached volume could be
|
||||
unmapped while still in use.
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- New Cinder driver for Seagate FC and iSCSI storage arrays.
|
||||
Reference in New Issue
Block a user