Merge "Dell SC: Add secondary DSM support"
This commit is contained in:
commit
538dc08d84
@ -7043,3 +7043,204 @@ class DellHttpClientTestCase(test.TestCase):
|
||||
mock_get.assert_called_once_with('https://localhost:3033/api/rest/url',
|
||||
headers=expected_headers,
|
||||
verify=False)
|
||||
|
||||
|
||||
class DellStorageCenterApiHelperTestCase(test.TestCase):
|
||||
|
||||
"""DellStorageCenterApiHelper test case
|
||||
|
||||
Class to test the Storage Center API helper using Mock.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(DellStorageCenterApiHelperTestCase, self).setUp()
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'open_connection')
|
||||
def test_setup_connection(self,
|
||||
mock_open_connection):
|
||||
config = mock.MagicMock()
|
||||
config.dell_sc_ssn = 12345
|
||||
config.san_ip = '192.168.0.101'
|
||||
config.san_login = 'username'
|
||||
config.san_password = 'password'
|
||||
config.dell_sc_volume_folder = 'a'
|
||||
config.dell_sc_server_folder = 'a'
|
||||
config.dell_sc_verify_cert = False
|
||||
config.san_port = 3033
|
||||
helper = dell_storagecenter_api.StorageCenterApiHelper(config, None,
|
||||
'FC')
|
||||
ret = helper._setup_connection()
|
||||
self.assertEqual(12345, ret.primaryssn)
|
||||
self.assertEqual(12345, ret.ssn)
|
||||
self.assertEqual('FibreChannel', ret.protocol)
|
||||
mock_open_connection.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'open_connection')
|
||||
def test_setup_connection_iscsi(self,
|
||||
mock_open_connection):
|
||||
config = mock.MagicMock()
|
||||
config.dell_sc_ssn = 12345
|
||||
config.san_ip = '192.168.0.101'
|
||||
config.san_login = 'username'
|
||||
config.san_password = 'password'
|
||||
config.dell_sc_volume_folder = 'a'
|
||||
config.dell_sc_server_folder = 'a'
|
||||
config.dell_sc_verify_cert = False
|
||||
config.san_port = 3033
|
||||
helper = dell_storagecenter_api.StorageCenterApiHelper(config, None,
|
||||
'iSCSI')
|
||||
ret = helper._setup_connection()
|
||||
self.assertEqual(12345, ret.primaryssn)
|
||||
self.assertEqual(12345, ret.ssn)
|
||||
self.assertEqual('Iscsi', ret.protocol)
|
||||
mock_open_connection.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'open_connection')
|
||||
def test_setup_connection_failover(self,
|
||||
mock_open_connection):
|
||||
config = mock.MagicMock()
|
||||
config.dell_sc_ssn = 12345
|
||||
config.san_ip = '192.168.0.101'
|
||||
config.san_login = 'username'
|
||||
config.san_password = 'password'
|
||||
config.dell_sc_volume_folder = 'a'
|
||||
config.dell_sc_server_folder = 'a'
|
||||
config.dell_sc_verify_cert = False
|
||||
config.san_port = 3033
|
||||
helper = dell_storagecenter_api.StorageCenterApiHelper(config, '67890',
|
||||
'iSCSI')
|
||||
ret = helper._setup_connection()
|
||||
self.assertEqual(12345, ret.primaryssn)
|
||||
self.assertEqual(67890, ret.ssn)
|
||||
self.assertEqual('Iscsi', ret.protocol)
|
||||
mock_open_connection.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApiHelper,
|
||||
'_setup_connection')
|
||||
def test_open_connection(self,
|
||||
mock_setup_connection):
|
||||
config = mock.MagicMock()
|
||||
config.dell_sc_ssn = 12345
|
||||
config.san_ip = '192.168.0.101'
|
||||
config.san_login = 'username'
|
||||
config.san_password = 'password'
|
||||
config.san_port = 3033
|
||||
helper = dell_storagecenter_api.StorageCenterApiHelper(config, None,
|
||||
'FC')
|
||||
mock_connection = mock.MagicMock()
|
||||
mock_connection.apiversion = '3.1'
|
||||
mock_setup_connection.return_value = mock_connection
|
||||
ret = helper.open_connection()
|
||||
self.assertEqual('3.1', ret.apiversion)
|
||||
self.assertEqual('192.168.0.101', helper.san_ip)
|
||||
self.assertEqual('username', helper.san_login)
|
||||
self.assertEqual('password', helper.san_password)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApiHelper,
|
||||
'_setup_connection')
|
||||
def test_open_connection_fail_no_secondary(self,
|
||||
mock_setup_connection):
|
||||
|
||||
config = mock.MagicMock()
|
||||
config.dell_sc_ssn = 12345
|
||||
config.san_ip = '192.168.0.101'
|
||||
config.san_login = 'username'
|
||||
config.san_password = 'password'
|
||||
config.san_port = 3033
|
||||
config.secondary_san_ip = ''
|
||||
helper = dell_storagecenter_api.StorageCenterApiHelper(config, None,
|
||||
'FC')
|
||||
mock_setup_connection.side_effect = (
|
||||
exception.VolumeBackendAPIException('abc'))
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
helper.open_connection)
|
||||
mock_setup_connection.assert_called_once_with()
|
||||
self.assertEqual('192.168.0.101', helper.san_ip)
|
||||
self.assertEqual('username', helper.san_login)
|
||||
self.assertEqual('password', helper.san_password)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApiHelper,
|
||||
'_setup_connection')
|
||||
def test_open_connection_secondary(self,
|
||||
mock_setup_connection):
|
||||
|
||||
config = mock.MagicMock()
|
||||
config.dell_sc_ssn = 12345
|
||||
config.san_ip = '192.168.0.101'
|
||||
config.san_login = 'username'
|
||||
config.san_password = 'password'
|
||||
config.san_port = 3033
|
||||
config.secondary_san_ip = '192.168.0.102'
|
||||
config.secondary_san_login = 'username2'
|
||||
config.secondary_san_password = 'password2'
|
||||
helper = dell_storagecenter_api.StorageCenterApiHelper(config, None,
|
||||
'FC')
|
||||
mock_connection = mock.MagicMock()
|
||||
mock_connection.apiversion = '3.1'
|
||||
mock_setup_connection.side_effect = [
|
||||
(exception.VolumeBackendAPIException('abc')), mock_connection]
|
||||
ret = helper.open_connection()
|
||||
self.assertEqual('3.1', ret.apiversion)
|
||||
self.assertEqual(2, mock_setup_connection.call_count)
|
||||
self.assertEqual('192.168.0.102', helper.san_ip)
|
||||
self.assertEqual('username2', helper.san_login)
|
||||
self.assertEqual('password2', helper.san_password)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApiHelper,
|
||||
'_setup_connection')
|
||||
def test_open_connection_fail_partial_secondary_config(
|
||||
self, mock_setup_connection):
|
||||
|
||||
config = mock.MagicMock()
|
||||
config.dell_sc_ssn = 12345
|
||||
config.san_ip = '192.168.0.101'
|
||||
config.san_login = 'username'
|
||||
config.san_password = 'password'
|
||||
config.san_port = 3033
|
||||
config.secondary_san_ip = '192.168.0.102'
|
||||
config.secondary_san_login = 'username2'
|
||||
config.secondary_san_password = ''
|
||||
helper = dell_storagecenter_api.StorageCenterApiHelper(config, None,
|
||||
'FC')
|
||||
mock_setup_connection.side_effect = (
|
||||
exception.VolumeBackendAPIException('abc'))
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
helper.open_connection)
|
||||
mock_setup_connection.assert_called_once_with()
|
||||
self.assertEqual('192.168.0.101', helper.san_ip)
|
||||
self.assertEqual('username', helper.san_login)
|
||||
self.assertEqual('password', helper.san_password)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApiHelper,
|
||||
'_setup_connection')
|
||||
def test_open_connection_to_secondary_and_back(self,
|
||||
mock_setup_connection):
|
||||
|
||||
config = mock.MagicMock()
|
||||
config.dell_sc_ssn = 12345
|
||||
config.san_ip = '192.168.0.101'
|
||||
config.san_login = 'username'
|
||||
config.san_password = 'password'
|
||||
config.san_port = 3033
|
||||
config.secondary_san_ip = '192.168.0.102'
|
||||
config.secondary_san_login = 'username2'
|
||||
config.secondary_san_password = 'password2'
|
||||
helper = dell_storagecenter_api.StorageCenterApiHelper(config, None,
|
||||
'FC')
|
||||
mock_connection = mock.MagicMock()
|
||||
mock_connection.apiversion = '3.1'
|
||||
mock_setup_connection.side_effect = [
|
||||
(exception.VolumeBackendAPIException('abc')), mock_connection,
|
||||
(exception.VolumeBackendAPIException('abc')), mock_connection]
|
||||
helper.open_connection()
|
||||
self.assertEqual('192.168.0.102', helper.san_ip)
|
||||
self.assertEqual('username2', helper.san_login)
|
||||
self.assertEqual('password2', helper.san_password)
|
||||
self.assertEqual(2, mock_setup_connection.call_count)
|
||||
helper.open_connection()
|
||||
self.assertEqual('192.168.0.101', helper.san_ip)
|
||||
self.assertEqual('username', helper.san_login)
|
||||
self.assertEqual('password', helper.san_password)
|
||||
|
@ -18,6 +18,7 @@ import os.path
|
||||
|
||||
import eventlet
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
import requests
|
||||
from simplejson import scanner
|
||||
import six
|
||||
@ -265,24 +266,47 @@ class StorageCenterApiHelper(object):
|
||||
self.active_backend_id = active_backend_id
|
||||
self.primaryssn = self.config.dell_sc_ssn
|
||||
self.storage_protocol = storage_protocol
|
||||
self.san_ip = self.config.san_ip
|
||||
self.san_login = self.config.san_login
|
||||
self.san_password = self.config.san_password
|
||||
self.san_port = self.config.dell_sc_api_port
|
||||
self.apiversion = '2.0'
|
||||
|
||||
def open_connection(self):
|
||||
"""Creates the StorageCenterApi object.
|
||||
def _swap_credentials(self):
|
||||
"""Change out to our secondary credentials
|
||||
|
||||
:return: StorageCenterApi object.
|
||||
:raises: VolumeBackendAPIException
|
||||
Or back to our primary creds.
|
||||
:return: True if swapped. False if no alt credentials supplied.
|
||||
"""
|
||||
connection = None
|
||||
LOG.info(_LI('open_connection to %(ssn)s at %(ip)s'),
|
||||
{'ssn': self.primaryssn,
|
||||
'ip': self.config.san_ip})
|
||||
if self.primaryssn:
|
||||
"""Open connection to REST API."""
|
||||
connection = StorageCenterApi(self.config.san_ip,
|
||||
self.config.dell_sc_api_port,
|
||||
self.config.san_login,
|
||||
self.config.san_password,
|
||||
if self.san_ip == self.config.san_ip:
|
||||
# Do we have a secondary IP and credentials?
|
||||
if (self.config.secondary_san_ip and
|
||||
self.config.secondary_san_login and
|
||||
self.config.secondary_san_password):
|
||||
self.san_ip = self.config.secondary_san_ip
|
||||
self.san_login = self.config.secondary_san_login
|
||||
self.san_password = self.config.secondary_san_password
|
||||
else:
|
||||
# Cannot swap.
|
||||
return False
|
||||
# Odds on this hasn't changed so no need to make setting this a
|
||||
# requirement.
|
||||
if self.config.secondary_sc_api_port:
|
||||
self.san_port = self.config.secondary_sc_api_port
|
||||
else:
|
||||
# These have to be set.
|
||||
self.san_ip = self.config.san_ip
|
||||
self.san_login = self.config.san_login
|
||||
self.san_password = self.config.san_password
|
||||
self.san_port = self.config.dell_sc_api_port
|
||||
return True
|
||||
|
||||
def _setup_connection(self):
|
||||
"""Attempts to open a connection to the storage center."""
|
||||
connection = StorageCenterApi(self.san_ip,
|
||||
self.san_port,
|
||||
self.san_login,
|
||||
self.san_password,
|
||||
self.config.dell_sc_verify_cert,
|
||||
self.apiversion)
|
||||
# This instance is for a single backend. That backend has a
|
||||
@ -300,8 +324,32 @@ class StorageCenterApiHelper(object):
|
||||
connection.ssn = int(self.active_backend_id)
|
||||
else:
|
||||
connection.ssn = self.primaryssn
|
||||
# Open connection.
|
||||
# Make the actual connection to the DSM.
|
||||
connection.open_connection()
|
||||
return connection
|
||||
|
||||
def open_connection(self):
|
||||
"""Creates the StorageCenterApi object.
|
||||
|
||||
:return: StorageCenterApi object.
|
||||
:raises: VolumeBackendAPIException
|
||||
"""
|
||||
connection = None
|
||||
LOG.info(_LI('open_connection to %(ssn)s at %(ip)s'),
|
||||
{'ssn': self.primaryssn,
|
||||
'ip': self.config.san_ip})
|
||||
if self.primaryssn:
|
||||
try:
|
||||
"""Open connection to REST API."""
|
||||
connection = self._setup_connection()
|
||||
except Exception:
|
||||
# If we have credentials to swap to we try it here.
|
||||
if self._swap_credentials():
|
||||
connection = self._setup_connection()
|
||||
else:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Failed to connect to the API. '
|
||||
'No backup DSM provided.'))
|
||||
# Save our api version for next time.
|
||||
if self.apiversion != connection.apiversion:
|
||||
LOG.info(_LI('open_connection: Updating API version to %s'),
|
||||
@ -334,9 +382,10 @@ class StorageCenterApi(object):
|
||||
2.5.0 - ManageableSnapshotsVD implemented.
|
||||
3.0.0 - ProviderID utilized.
|
||||
3.1.0 - Failback Supported.
|
||||
3.3.0 - Support for a secondary DSM.
|
||||
"""
|
||||
|
||||
APIDRIVERVERSION = '3.1.0'
|
||||
APIDRIVERVERSION = '3.3.0'
|
||||
|
||||
def __init__(self, host, port, user, password, verify, apiversion):
|
||||
"""This creates a connection to Dell SC or EM.
|
||||
|
@ -42,7 +42,20 @@ common_opts = [
|
||||
help='Name of the volume folder to use on the Storage Center'),
|
||||
cfg.BoolOpt('dell_sc_verify_cert',
|
||||
default=False,
|
||||
help='Enable HTTPS SC certificate verification.')
|
||||
help='Enable HTTPS SC certificate verification'),
|
||||
cfg.StrOpt('secondary_san_ip',
|
||||
default='',
|
||||
help='IP address of secondary DSM controller'),
|
||||
cfg.StrOpt('secondary_san_login',
|
||||
default='Admin',
|
||||
help='Secondary DSM user name'),
|
||||
cfg.StrOpt('secondary_san_password',
|
||||
default='',
|
||||
help='Secondary DSM user password name',
|
||||
secret=True),
|
||||
cfg.PortOpt('secondary_sc_api_port',
|
||||
default=3033,
|
||||
help='Secondary Dell API port')
|
||||
]
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -594,6 +607,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
|
||||
data['storage_protocol'] = self.storage_protocol
|
||||
data['reserved_percentage'] = 0
|
||||
data['consistencygroup_support'] = True
|
||||
data['thin_provisioning_support'] = True
|
||||
totalcapacity = storageusage.get('availableSpace')
|
||||
totalcapacitygb = self._bytes_to_gb(totalcapacity)
|
||||
data['total_capacity_gb'] = totalcapacitygb
|
||||
|
Loading…
Reference in New Issue
Block a user