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',
|
mock_get.assert_called_once_with('https://localhost:3033/api/rest/url',
|
||||||
headers=expected_headers,
|
headers=expected_headers,
|
||||||
verify=False)
|
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
|
import eventlet
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import excutils
|
||||||
import requests
|
import requests
|
||||||
from simplejson import scanner
|
from simplejson import scanner
|
||||||
import six
|
import six
|
||||||
@ -265,8 +266,68 @@ class StorageCenterApiHelper(object):
|
|||||||
self.active_backend_id = active_backend_id
|
self.active_backend_id = active_backend_id
|
||||||
self.primaryssn = self.config.dell_sc_ssn
|
self.primaryssn = self.config.dell_sc_ssn
|
||||||
self.storage_protocol = storage_protocol
|
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'
|
self.apiversion = '2.0'
|
||||||
|
|
||||||
|
def _swap_credentials(self):
|
||||||
|
"""Change out to our secondary credentials
|
||||||
|
|
||||||
|
Or back to our primary creds.
|
||||||
|
:return: True if swapped. False if no alt credentials supplied.
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
# few items of information we should save rather than passing them
|
||||||
|
# about.
|
||||||
|
connection.vfname = self.config.dell_sc_volume_folder
|
||||||
|
connection.sfname = self.config.dell_sc_server_folder
|
||||||
|
# Our primary SSN doesn't change
|
||||||
|
connection.primaryssn = self.primaryssn
|
||||||
|
if self.storage_protocol == 'FC':
|
||||||
|
connection.protocol = 'FibreChannel'
|
||||||
|
# Set appropriate ssn and failover state.
|
||||||
|
if self.active_backend_id:
|
||||||
|
# active_backend_id is a string. Convert to int.
|
||||||
|
connection.ssn = int(self.active_backend_id)
|
||||||
|
else:
|
||||||
|
connection.ssn = self.primaryssn
|
||||||
|
# Make the actual connection to the DSM.
|
||||||
|
connection.open_connection()
|
||||||
|
return connection
|
||||||
|
|
||||||
def open_connection(self):
|
def open_connection(self):
|
||||||
"""Creates the StorageCenterApi object.
|
"""Creates the StorageCenterApi object.
|
||||||
|
|
||||||
@ -278,30 +339,17 @@ class StorageCenterApiHelper(object):
|
|||||||
{'ssn': self.primaryssn,
|
{'ssn': self.primaryssn,
|
||||||
'ip': self.config.san_ip})
|
'ip': self.config.san_ip})
|
||||||
if self.primaryssn:
|
if self.primaryssn:
|
||||||
"""Open connection to REST API."""
|
try:
|
||||||
connection = StorageCenterApi(self.config.san_ip,
|
"""Open connection to REST API."""
|
||||||
self.config.dell_sc_api_port,
|
connection = self._setup_connection()
|
||||||
self.config.san_login,
|
except Exception:
|
||||||
self.config.san_password,
|
# If we have credentials to swap to we try it here.
|
||||||
self.config.dell_sc_verify_cert,
|
if self._swap_credentials():
|
||||||
self.apiversion)
|
connection = self._setup_connection()
|
||||||
# This instance is for a single backend. That backend has a
|
else:
|
||||||
# few items of information we should save rather than passing them
|
with excutils.save_and_reraise_exception():
|
||||||
# about.
|
LOG.error(_LE('Failed to connect to the API. '
|
||||||
connection.vfname = self.config.dell_sc_volume_folder
|
'No backup DSM provided.'))
|
||||||
connection.sfname = self.config.dell_sc_server_folder
|
|
||||||
# Our primary SSN doesn't change
|
|
||||||
connection.primaryssn = self.primaryssn
|
|
||||||
if self.storage_protocol == 'FC':
|
|
||||||
connection.protocol = 'FibreChannel'
|
|
||||||
# Set appropriate ssn and failover state.
|
|
||||||
if self.active_backend_id:
|
|
||||||
# active_backend_id is a string. Convert to int.
|
|
||||||
connection.ssn = int(self.active_backend_id)
|
|
||||||
else:
|
|
||||||
connection.ssn = self.primaryssn
|
|
||||||
# Open connection.
|
|
||||||
connection.open_connection()
|
|
||||||
# Save our api version for next time.
|
# Save our api version for next time.
|
||||||
if self.apiversion != connection.apiversion:
|
if self.apiversion != connection.apiversion:
|
||||||
LOG.info(_LI('open_connection: Updating API version to %s'),
|
LOG.info(_LI('open_connection: Updating API version to %s'),
|
||||||
@ -334,9 +382,10 @@ class StorageCenterApi(object):
|
|||||||
2.5.0 - ManageableSnapshotsVD implemented.
|
2.5.0 - ManageableSnapshotsVD implemented.
|
||||||
3.0.0 - ProviderID utilized.
|
3.0.0 - ProviderID utilized.
|
||||||
3.1.0 - Failback Supported.
|
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):
|
def __init__(self, host, port, user, password, verify, apiversion):
|
||||||
"""This creates a connection to Dell SC or EM.
|
"""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'),
|
help='Name of the volume folder to use on the Storage Center'),
|
||||||
cfg.BoolOpt('dell_sc_verify_cert',
|
cfg.BoolOpt('dell_sc_verify_cert',
|
||||||
default=False,
|
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__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -594,6 +607,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
|
|||||||
data['storage_protocol'] = self.storage_protocol
|
data['storage_protocol'] = self.storage_protocol
|
||||||
data['reserved_percentage'] = 0
|
data['reserved_percentage'] = 0
|
||||||
data['consistencygroup_support'] = True
|
data['consistencygroup_support'] = True
|
||||||
|
data['thin_provisioning_support'] = True
|
||||||
totalcapacity = storageusage.get('availableSpace')
|
totalcapacity = storageusage.get('availableSpace')
|
||||||
totalcapacitygb = self._bytes_to_gb(totalcapacity)
|
totalcapacitygb = self._bytes_to_gb(totalcapacity)
|
||||||
data['total_capacity_gb'] = totalcapacitygb
|
data['total_capacity_gb'] = totalcapacitygb
|
||||||
|
Loading…
Reference in New Issue
Block a user