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:
Chris M 2019-07-17 05:45:48 +00:00
parent 1962d17cad
commit e18f05e735
23 changed files with 696 additions and 352 deletions

View File

@ -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,

View File

@ -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]

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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.")

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,3 @@
---
features:
- New Cinder driver for Seagate FC and iSCSI storage arrays.