Merge "Dell SC: Add secondary DSM support"

This commit is contained in:
Jenkins 2016-07-20 14:22:50 +00:00 committed by Gerrit Code Review
commit 538dc08d84
3 changed files with 290 additions and 26 deletions

View File

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

View File

@ -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,8 +266,68 @@ 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 _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):
"""Creates the StorageCenterApi object.
@ -278,30 +339,17 @@ class StorageCenterApiHelper(object):
{'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,
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
# Open connection.
connection.open_connection()
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.

View File

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