Create Seagate driver from dothill driver
This patch creates a new 'supported' driver for Seagate (STX) FC and iSCSI arrays by renaming and re-enabling the old 'unsupported' dothill driver. Other than marking the driver as 'supported', this patch contains no changes in functionality except for multiattach fixes from change I47f02729437cabab92ccc553a4c60d0c0a796952 needed to pass CI and deprecation of vendor-specific options requested by the core team. Other drivers which referenced the dothill driver are modified to use the Seagate class names, so users of those drivers will not be affected except for option-deprecation warnings. Change-Id: I3115ae296ae6b5702c7a8fa39249b8735542e17e
This commit is contained in:
parent
1962d17cad
commit
e18f05e735
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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:
|
@ -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
|
@ -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.
|
Loading…
Reference in New Issue
Block a user