Add https options and minor code changes

Adds https options x_verify_certificate and x_verify_certificate_path
in DotHill and rebranded drivers.

Minor cosmetic changes like replacing 'realstor' with 'virtual'
and 'wbi' with 'api'. Adding default value for 'x_backend_type'
and 'x_api_protocol' options. Minor code optimization for
finding array iSCSI addresses.

Here 'x' stands for dothill or hpmsa or lenovo.

DocImpact
Change-Id: I7a5924bbd56053aee6d652842b48e913c54ae7dd
Closes-Bug: #1486160
Co-Authored-By: Lakshman <lakshminarayanat@vedams.com>
Co-Authored-By: Chris Maio <chris.maio@dothill.com>
This commit is contained in:
nikeshm 2015-08-22 02:09:51 +05:30
parent fbc59713eb
commit 2c5f154171
9 changed files with 105 additions and 62 deletions

View File

@ -51,7 +51,7 @@ response_stats_linear = '''<RESPONSE><OBJECT basetype="virtual-disks">
<PROPERTY name="size-numeric">3863830528</PROPERTY>
<PROPERTY name="freespace-numeric">3863830528</PROPERTY>
</OBJECT></RESPONSE>'''
response_stats_realstor = '''<RESPONSE><OBJECT basetype="pools">
response_stats_virtual = '''<RESPONSE><OBJECT basetype="pools">
<PROPERTY name="total-size-numeric">3863830528</PROPERTY>
<PROPERTY name="total-avail-numeric">3863830528</PROPERTY>
</OBJECT></RESPONSE>'''
@ -87,7 +87,7 @@ response_ports = '''<RESPONSE>
</RESPONSE>'''
response_ports_linear = response_ports % {'ip': 'primary-ip-address'}
response_ports_realstor = response_ports % {'ip': 'ip-address'}
response_ports_virtual = response_ports % {'ip': 'ip-address'}
invalid_xml = '''<RESPONSE></RESPONSE>'''
@ -146,8 +146,9 @@ class TestDotHillClient(test.TestCase):
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.protocol, self.ssl_verify)
@mock.patch('requests.get')
def test_login(self, mock_requests_get):
@ -219,13 +220,13 @@ class TestDotHillClient(test.TestCase):
stats = {'free_capacity_gb': 1979,
'total_capacity_gb': 1979}
linear = etree.XML(response_stats_linear)
realstor = etree.XML(response_stats_realstor)
mock_request.side_effect = [linear, realstor]
virtual = etree.XML(response_stats_virtual)
mock_request.side_effect = [linear, virtual]
self.assertEqual(stats, self.client.backend_stats('OpenStack',
'linear'))
self.assertEqual(stats, self.client.backend_stats('OpenStack',
'realstor'))
self.assertEqual(stats, self.client.backend_stats('A',
'virtual'))
@mock.patch.object(dothill.DotHillClient, '_request')
def test_get_lun(self, mock_request):
@ -266,10 +267,10 @@ class TestDotHillClient(test.TestCase):
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),
etree.XML(response_ports_realstor)]
ret = self.client.get_active_iscsi_target_portals('linear')
etree.XML(response_ports_virtual)]
ret = self.client.get_active_iscsi_target_portals()
self.assertEqual(portals, ret)
ret = self.client.get_active_iscsi_target_portals('realstor')
ret = self.client.get_active_iscsi_target_portals()
self.assertEqual(portals, ret)
@ -279,7 +280,7 @@ class FakeConfiguration1(object):
san_ip = '10.0.0.1'
san_login = 'manage'
san_password = '!manage'
dothill_wbi_protocol = 'http'
dothill_api_protocol = 'http'
def safe_get(self, key):
return 'fakevalue'

View File

@ -30,11 +30,12 @@ LOG = logging.getLogger(__name__)
class DotHillClient(object):
def __init__(self, host, login, password, protocol):
def __init__(self, host, login, password, protocol, ssl_verify):
self._login = login
self._password = password
self._base_url = "%s://%s/api" % (protocol, host)
self._session_key = None
self.ssl_verify = ssl_verify
def _get_auth_token(self, xml):
"""Parse an XML authentication reply to extract the session key."""
@ -53,7 +54,7 @@ class DotHillClient(object):
url = self._base_url + "/login/" + digest
try:
xml = requests.get(url)
xml = requests.get(url, verify=self.ssl_verify)
except requests.exceptions.RequestException:
raise exception.DotHillConnectionError
@ -96,7 +97,7 @@ class DotHillClient(object):
url = self._build_request_url(path, *args, **kargs)
headers = {'dataType': 'api', 'sessionKey': self._session_key}
try:
xml = requests.get(url, headers=headers)
xml = requests.get(url, headers=headers, verify=self.ssl_verify)
tree = etree.XML(xml.text.encode('utf8'))
except Exception:
raise exception.DotHillConnectionError
@ -109,7 +110,7 @@ class DotHillClient(object):
def logout(self):
url = self._base_url + '/exit'
try:
requests.get(url)
requests.get(url, verify=self.ssl_verify)
return True
except Exception:
return False
@ -275,10 +276,10 @@ class DotHillClient(object):
return host_status
def _safe_hostname(self, hostname):
"""DotHill hostname restrictions.
"""Modify an initiator name to match firmware requirements.
A host name cannot include " , \ in linear and " , < > \ in realstor
and can have a max of 15 bytes in linear and 32 bytes in realstor.
Initiator name cannot include certain characters and cannot exceed
15 bytes in 'T' firmware (32 bytes in 'G' firmware).
"""
for ch in [',', '"', '\\', '<', '>']:
if ch in hostname:
@ -288,16 +289,14 @@ class DotHillClient(object):
index = 15
return hostname[:index]
def get_active_iscsi_target_portals(self, backend_type):
def get_active_iscsi_target_portals(self):
# This function returns {'ip': status,}
portals = {}
prop = ""
prop = 'ip-address'
tree = self._request("/show/ports")
if backend_type == "linear":
prop = "primary-ip-address"
else:
prop = "ip-address"
for el in tree.xpath("//PROPERTY[@name='primary-ip-address']"):
prop = 'primary-ip-address'
break
iscsi_ips = [ip.text for ip in tree.xpath(
"//PROPERTY[@name='%s']" % prop)]
if not iscsi_ips:

View File

@ -32,20 +32,28 @@ LOG = logging.getLogger(__name__)
common_opt = [
cfg.StrOpt('dothill_backend_name',
default='OpenStack',
help="VDisk or Pool name to use for volume creation."),
default='A',
help="Pool or Vdisk name to use for volume creation."),
cfg.StrOpt('dothill_backend_type',
choices=['linear', 'realstor'],
help="linear (for VDisk) or realstor (for Pool)."),
cfg.StrOpt('dothill_wbi_protocol',
choices=['linear', 'virtual'],
default='virtual',
help="linear (for Vdisk) or virtual (for Pool)."),
cfg.StrOpt('dothill_api_protocol',
choices=['http', 'https'],
help="DotHill web interface protocol."),
default='https',
help="DotHill API interface protocol."),
cfg.BoolOpt('dothill_verify_certificate',
default=False,
help="Whether to verify DotHill array SSL certificate."),
cfg.StrOpt('dothill_verify_certificate_path',
default=None,
help="DotHill array SSL certificate path."),
]
iscsi_opt = [
cfg.ListOpt('dothill_iscsi_ips',
default=[],
help="List of comma separated target iSCSI IP addresses."),
help="List of comma-separated target iSCSI IP addresses."),
]
CONF = cfg.CONF
@ -63,10 +71,16 @@ class DotHillCommon(object):
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.config.dothill_wbi_protocol)
self.api_protocol,
ssl_verify)
def get_version(self):
return self.VERSION
@ -75,7 +89,7 @@ class DotHillCommon(object):
self.client_login()
self._validate_backend()
if (self.backend_type == "linear" or
(self.backend_type == "realstor" and
(self.backend_type == "virtual" and
self.backend_name not in ['A', 'B'])):
self._get_owner_info(self.backend_name)
self._get_serial_number()
@ -194,7 +208,7 @@ class DotHillCommon(object):
raise exception.VolumeAttached(volume_id=volume['id'])
def create_cloned_volume(self, volume, src_vref):
if self.backend_type == "realstor" and self.backend_name in ["A", "B"]:
if self.backend_type == "virtual" and self.backend_name in ["A", "B"]:
msg = _("Create volume from volume(clone) does not have support "
"for virtual pool A and B.")
LOG.error(msg)
@ -223,7 +237,7 @@ class DotHillCommon(object):
self.client_logout()
def create_volume_from_snapshot(self, volume, snapshot):
if self.backend_type == "realstor" and self.backend_name in ["A", "B"]:
if self.backend_type == "virtual" and self.backend_name in ["A", "B"]:
msg = _('Create volume from snapshot does not have support '
'for virtual pool A and B.')
LOG.error(msg)
@ -290,7 +304,7 @@ class DotHillCommon(object):
self.backend_type)
pool.update(backend_stats)
if (self.backend_type == "linear" or
(self.backend_type == "realstor" and
(self.backend_type == "virtual" and
self.backend_name not in ['A', 'B'])):
pool['location_info'] = ('%s:%s:%s:%s' %
(src_type,
@ -362,8 +376,7 @@ class DotHillCommon(object):
def get_active_iscsi_target_portals(self):
try:
return self.client.get_active_iscsi_target_portals(
self.backend_type)
return self.client.get_active_iscsi_target_portals()
except exception.DotHillRequestError as ex:
LOG.exception(_LE("Error getting active ISCSI target portals."))
raise exception.Invalid(ex)

View File

@ -33,7 +33,7 @@ class DotHillFCDriver(cinder.volume.driver.FibreChannelDriver):
cinder/volume/drivers/san/hp"
1.0 - Version developed for DotHill arrays with the following
modifications:
- added support for v3 API(realstor feature)
- added support for v3 API(virtual pool feature)
- added support for retype volume
- added support for manage/unmanage volume
- added initiator target mapping in FC zoning

View File

@ -39,7 +39,7 @@ class DotHillISCSIDriver(cinder.volume.driver.ISCSIDriver):
modifications:
- added iSCSI support
- added CHAP support in iSCSI
- added support for v3 API(realstor feature)
- added support for v3 API(virtual pool feature)
- added support for retype volume
- added support for manage/unmanage volume
- added https support

View File

@ -19,5 +19,6 @@ from cinder.volume.drivers.dothill import dothill_client
class LenovoClient(dothill_client.DotHillClient):
def __init__(self, host, login, password, protocol):
super(LenovoClient, self).__init__(host, login, password, protocol)
def __init__(self, host, login, password, protocol, ssl_verify):
super(LenovoClient, self).__init__(host, login, password, protocol,
ssl_verify)

View File

@ -21,20 +21,28 @@ from cinder.volume.drivers.lenovo import lenovo_client
common_opt = [
cfg.StrOpt('lenovo_backend_name',
default='OpenStack',
help="VDisk or Pool name to use for volume creation."),
default='A',
help="Pool or Vdisk name to use for volume creation."),
cfg.StrOpt('lenovo_backend_type',
choices=['linear', 'realstor'],
help="linear (for VDisk) or realstor (for Pool)."),
cfg.StrOpt('lenovo_wbi_protocol',
choices=['linear', 'virtual'],
default='virtual',
help="linear (for VDisk) or virtual (for Pool)."),
cfg.StrOpt('lenovo_api_protocol',
choices=['http', 'https'],
help="Lenovo web interface protocol."),
default='https',
help="Lenovo api interface protocol."),
cfg.BoolOpt('lenovo_verify_certificate',
default=False,
help="Whether to verify Lenovo array SSL certificate."),
cfg.StrOpt('lenovo_verify_certificate_path',
default=None,
help="Lenovo array SSL certificate path.")
]
iscsi_opt = [
cfg.ListOpt('lenovo_iscsi_ips',
default=[],
help="List of comma separated target iSCSI IP addresses."),
help="List of comma-separated target iSCSI IP addresses."),
]
CONF = cfg.CONF
@ -50,8 +58,13 @@ class LenovoCommon(dothill_common.DotHillCommon):
self.vendor_name = "Lenovo"
self.backend_name = self.config.lenovo_backend_name
self.backend_type = self.config.lenovo_backend_type
self.wbi_protocol = self.config.lenovo_wbi_protocol
self.api_protocol = self.config.lenovo_api_protocol
ssl_verify = False
if (self.api_protocol == 'https' and
self.config.lenovo_verify_certificate):
ssl_verify = self.config.lenovo_verify_certificate_path or True
self.client = lenovo_client.LenovoClient(self.config.san_ip,
self.config.san_login,
self.config.san_password,
self.wbi_protocol)
self.api_protocol,
ssl_verify)

View File

@ -19,6 +19,6 @@ from cinder.volume.drivers.dothill import dothill_client
class HPMSAClient(dothill_client.DotHillClient):
def __init__(self, host, login, password, protocol):
def __init__(self, host, login, password, protocol, ssl_verify):
super(HPMSAClient, self).__init__(host, login, password,
protocol)
protocol, ssl_verify)

View File

@ -21,20 +21,29 @@ from cinder.volume.drivers.san.hp import hpmsa_client
common_opt = [
cfg.StrOpt('hpmsa_backend_name',
default='OpenStack',
help="VDisk or Pool name to use for volume creation."),
default='A',
help="Pool or Vdisk name to use for volume creation."),
cfg.StrOpt('hpmsa_backend_type',
choices=['linear', 'realstor'],
help="linear (for VDisk) or realstor (for Pool)."),
cfg.StrOpt('hpmsa_wbi_protocol',
choices=['linear', 'virtual'],
default='virtual',
help="linear (for Vdisk) or virtual (for Pool)."),
cfg.StrOpt('hpmsa_api_protocol',
choices=['http', 'https'],
help="HPMSA web interface protocol."),
default='https',
help="HPMSA API interface protocol."),
cfg.BoolOpt('hpmsa_verify_certificate',
default=False,
help="Whether to verify HPMSA array SSL certificate."),
cfg.StrOpt('hpmsa_verify_certificate_path',
default=None,
help="HPMSA array SSL certificate path."),
]
iscsi_opt = [
cfg.ListOpt('hpmsa_iscsi_ips',
default=[],
help="List of comma separated target iSCSI IP addresses."),
help="List of comma-separated target iSCSI IP addresses."),
]
CONF = cfg.CONF
@ -50,7 +59,14 @@ class HPMSACommon(dothill_common.DotHillCommon):
self.vendor_name = "HPMSA"
self.backend_name = self.config.hpmsa_backend_name
self.backend_type = self.config.hpmsa_backend_type
self.api_protocol = self.config.hpmsa_api_protocol
ssl_verify = False
if (self.api_protocol == 'https' and
self.config.hpmsa_verify_certificate):
ssl_verify = self.config.hpmsa_verify_certificate_path or True
self.client = hpmsa_client.HPMSAClient(self.config.san_ip,
self.config.san_login,
self.config.san_password,
self.config.hpmsa_wbi_protocol)
self.api_protocol,
ssl_verify)