Skip to content

Commit

Permalink
Merge pull request #28 from yunqifeng/master
Browse files Browse the repository at this point in the history
update fusionstorage driver for openstack
  • Loading branch information
WBhanshy authored Apr 14, 2022
2 parents babc457 + 7dc7957 commit fd0449f
Show file tree
Hide file tree
Showing 48 changed files with 6,774 additions and 60 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions Cinder/Stein/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@
CONF_POOLS = "dsware_storage_pools"
CONF_PWD = "san_password"
CONF_USER = "san_login"
CONF_IP = "san_ip"
CONF_PORT = "san_port"
CONF_NEW_POOLS = "storage_pools"
CONF_STORAGE_CA_FILEPATH = "storage_ca_filepath"
CONF_STORAGE_KEY_FILEPATH = "storage_key_filepath"
CONF_STORAGE_CERT_FILEPATH = "storage_cert_filepath"
CONF_STORAGE_SSL_TWO_WAY_AUTH = "storage_ssl_two_way_auth"

QOS_MUST_SET = ["maxIOPS", "maxMBPS"]
QOS_KEYS = ["maxIOPS", "maxMBPS", "total_iops_sec", "total_bytes_sec"]
Expand Down
76 changes: 68 additions & 8 deletions Cinder/Stein/dsware.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
cfg.StrOpt('dsware_storage_pools',
default="",
help='The list of pools on the FusionStorage array, the '
'semicolon(;) was used to split the storage pools, '
'semicolon(;) is used to split the storage pools, '
'"dsware_storage_pools = xxx1; xxx2; xxx3"'),
cfg.ListOpt('target_ips',
default=[],
Expand All @@ -71,7 +71,40 @@
cfg.BoolOpt('use_ipv6',
default=False,
help='Whether to return target_portal and target_iqn in '
'IPV6 format')
'IPV6 format'),
cfg.BoolOpt('force_delete_volume',
default=False,
help='When deleting a LUN, if the LUN is in the mapping view,'
' whether to delete it forcibly'),
cfg.StrOpt('san_ip',
default='',
help='The ip address of FusionStorage array. For example, '
'"san_ip=xxx"'),
cfg.StrOpt('san_port',
default='',
help='The port of FusionStorage array. For example, '
'"san_port=xxx"'),
cfg.StrOpt('storage_pools',
default="",
help='The list of pools on the FusionStorage array, the '
'semicolon(;) is used to split the storage pools, '
'"storage_pools = xxx1; xxx2; xxx3"'),
cfg.IntOpt('iscsi_link_count',
default=4,
help='Number of iSCSI links in an iSCSI network. '
'The default value is 4.'),
cfg.BoolOpt('storage_ssl_two_way_auth',
default=False,
help='Whether to use mutual authentication.'),
cfg.StrOpt('storage_ca_filepath',
default='',
help='CA certificate directory.'),
cfg.StrOpt('storage_cert_filepath',
default='',
help='Client certificate directory.'),
cfg.StrOpt('storage_key_filepath',
default='',
help='Client key directory.'),
]

CONF = cfg.CONF
Expand All @@ -80,7 +113,7 @@

@interface.volumedriver
class DSWAREBaseDriver(driver.VolumeDriver):
VERSION = '2.3.RC1'
VERSION = '2.3.RC3'
CI_WIKI_NAME = 'Huawei_FusionStorage_CI'

def __init__(self, *args, **kwargs):
Expand All @@ -105,13 +138,26 @@ def get_driver_options():

def do_setup(self, context):
self.conf.update_config_value()
self.conf.check_ssl_two_way_config_valid()
url_str = self.configuration.san_address
url_user = self.configuration.san_user
url_password = self.configuration.san_password

self.client = fs_client.RestCommon(
fs_address=url_str, fs_user=url_user,
fs_password=url_password)
mutual_authentication = {
"storage_ca_filepath": self.configuration.storage_ca_filepath,
"storage_key_filepath": self.configuration.storage_key_filepath,
"storage_cert_filepath": self.configuration.storage_cert_filepath,
"storage_ssl_two_way_auth":
self.configuration.storage_ssl_two_way_auth
}

extend_conf = {
"mutual_authentication": mutual_authentication
}

self.client = fs_client.RestCommon(fs_address=url_str,
fs_user=url_user,
fs_password=url_password,
**extend_conf)
self.client.login()
self.fs_qos = fs_qos.FusionStorageQoS(self.client)

Expand All @@ -131,7 +177,7 @@ def _update_pool_stats(self):
backend_name = self.configuration.safe_get(
'volume_backend_name') or self.__class__.__name__
data = {"volume_backend_name": backend_name,
"driver_version": "2.3.RC1",
"driver_version": "2.3.RC3",
"pools": [],
"vendor_name": "Huawei"
}
Expand Down Expand Up @@ -183,6 +229,19 @@ def _check_volume_exist(self, volume):
if result:
return result

def _check_volume_mapped(self, vol_name):
host_list = self.client.get_host_by_volume(vol_name)
if ((len(host_list) > 1 and self.conf.force_delete_volume) or
len(host_list) == 1):
msg = ('Volume %s has been mapped to host.'
' Now force to delete it') % vol_name
LOG.warning(msg)
for host in host_list:
self.client.unmap_volume_from_host(host['hostName'], vol_name)
elif len(host_list) > 1 and not self.conf.force_delete_volume:
msg = 'Volume %s has been mapped to more than one host' % vol_name
self._raise_exception(msg)

@staticmethod
def _raise_exception(msg):
LOG.error(msg)
Expand Down Expand Up @@ -257,6 +316,7 @@ def create_volume(self, volume):
def delete_volume(self, volume):
vol_name = self._get_vol_name(volume)
if self._check_volume_exist(volume):
self._check_volume_mapped(vol_name)
self.fs_qos.remove(vol_name)
self.client.delete_volume(vol_name=vol_name)

Expand Down
42 changes: 38 additions & 4 deletions Cinder/Stein/fs_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import requests
import six
import time
from requests.adapters import HTTPAdapter

from cinder import exception
from cinder.i18n import _
Expand All @@ -26,30 +27,45 @@
LOG = logging.getLogger(__name__)


class HostNameIgnoringAdapter(HTTPAdapter):
def cert_verify(self, conn, url, verify, cert):
conn.assert_hostname = False
return super(HostNameIgnoringAdapter, self).cert_verify(
conn, url, verify, cert)


class RestCommon(object):
def __init__(self, fs_address, fs_user, fs_password):
def __init__(self, fs_address, fs_user, fs_password, **extend_conf):
self.address = fs_address
self.user = fs_user
self.password = fs_password

self.session = None
self.token = None
self.version = None

self.init_http_head()
mutual_authentication = extend_conf.get("mutual_authentication")
self.init_http_head(mutual_authentication)

LOG.warning("Suppressing requests library SSL Warnings")
requests.packages.urllib3.disable_warnings(
requests.packages.urllib3.exceptions.InsecureRequestWarning)
requests.packages.urllib3.disable_warnings(
requests.packages.urllib3.exceptions.InsecurePlatformWarning)

def init_http_head(self):
def init_http_head(self, mutual_authentication=None):
self.session = requests.Session()
self.session.headers.update({
"Content-Type": "application/json;charset=UTF-8",
})
self.session.verify = False
self.session.mount(self.address, HostNameIgnoringAdapter())

if mutual_authentication.get("storage_ssl_two_way_auth"):
self.session.verify = \
mutual_authentication.get("storage_ca_filepath")
self.session.cert = \
(mutual_authentication.get("storage_cert_filepath"),
mutual_authentication.get("storage_key_filepath"))

def _construct_url(self, url, get_version, get_system_time):
if get_system_time:
Expand Down Expand Up @@ -712,3 +728,21 @@ def delete_iscsi_host_relation(self, host_name, ip_list):
return None
else:
raise

def get_iscsi_links_info(self, iscsi_link_count, pool_list):
iscsi_ips = []
url = "/dsware/service/iscsi/queryVbsIscsiLinks"
params = {"amount": iscsi_link_count,
"poolList": pool_list}
try:
result = self.call(url, "POST", params, get_system_time=True)
self._assert_rest_result(
result, _("Get iscsi host relation session error."))
except Exception as err:
if constants.URL_NOT_FOUND in six.text_type(err):
return iscsi_ips
else:
raise

return [iscsi["ip"] for iscsi in result.get("iscsiLinks", [])
if iscsi.get("ip")]
25 changes: 23 additions & 2 deletions Cinder/Stein/fs_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,14 @@ def _assert_text_result(self, text, mess):

def _san_address(self):
address = self.configuration.safe_get(constants.CONF_ADDRESS)
self._assert_text_result(address, mess=constants.CONF_ADDRESS)
if not address:
san_ip = self.configuration.safe_get(constants.CONF_IP)
san_port = self.configuration.safe_get(constants.CONF_PORT)
if san_ip and san_port:
address = "https://" + san_ip + ":" + san_port
mess = (constants.CONF_ADDRESS + ' or ' + constants.CONF_IP + ' or ' +
constants.CONF_PORT)
self._assert_text_result(address, mess=mess)
setattr(self.configuration, 'san_address', address)

def _decode_text(self, text):
Expand All @@ -114,7 +121,10 @@ def _san_password(self):

def _pools_name(self):
pools_name = self.configuration.safe_get(constants.CONF_POOLS)
self._assert_text_result(pools_name, mess=constants.CONF_POOLS)
if not pools_name:
pools_name = self.configuration.safe_get(constants.CONF_NEW_POOLS)
mess = constants.CONF_POOLS + ' or ' + constants.CONF_NEW_POOLS
self._assert_text_result(pools_name, mess=mess)
pools = set(x.strip() for x in pools_name.split(';') if x.strip())
if not pools:
msg = _('No valid storage pool configured.')
Expand All @@ -126,3 +136,14 @@ def _manager_ip(self):
manager_ips = self.configuration.safe_get(constants.CONF_MANAGER_IP)
self._assert_text_result(manager_ips, mess=constants.CONF_MANAGER_IP)
setattr(self.configuration, 'manager_ips', manager_ips)

def check_ssl_two_way_config_valid(self):
if not self.configuration.storage_ssl_two_way_auth:
return

self._assert_text_result(self.configuration.storage_cert_filepath,
mess=constants.CONF_STORAGE_CERT_FILEPATH)
self._assert_text_result(self.configuration.storage_ca_filepath,
mess=constants.CONF_STORAGE_CA_FILEPATH)
self._assert_text_result(self.configuration.storage_key_filepath,
mess=constants.CONF_STORAGE_KEY_FILEPATH)
31 changes: 30 additions & 1 deletion Cinder/Stein/fs_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,43 @@ def _find_iscsi_ips(self, host_name):
% (target_ips, target_iqns))
return target_ips, target_iqns

def _find_iscsi_ips_from_storage(self, host_name):
valid_iscsi_ips, valid_node_ips = fs_utils.get_valid_iscsi_info(
self.client)
target_ips, target_iqns = fs_utils.get_iscsi_info_from_host(
self.client, host_name, valid_iscsi_ips)

if not target_ips:
iscsi_links = self.client.get_iscsi_links_info(
self.configuration.iscsi_link_count,
self.configuration.pools_name)
(node_ips, target_ips, target_iqns
) = fs_utils.get_iscsi_info_from_storage(
iscsi_links, self.configuration.use_ipv6,
valid_iscsi_ips, valid_node_ips)
if target_ips:
self.client.add_iscsi_host_relation(host_name, node_ips)

if not target_ips:
msg = _("Can not find a valid target ip")
LOG.warning(msg)
raise exception.InvalidInput(msg)

LOG.info("Get iscsi target info, target ips: %s, target iqns: %s",
target_ips, target_iqns)
return target_ips, target_iqns

def execute(self, host_name, vol_name, multipath):
LOG.info("Get ISCSI initialize connection properties.")
target_lun = fs_utils.get_target_lun(self.client, host_name, vol_name)

if self.configuration.iscsi_manager_groups:
target_ips, target_iqns = self._find_iscsi_ips(host_name)
else:
elif self.configuration.target_ips:
target_ips, target_iqns = self._find_target_ips()
else:
target_ips, target_iqns = self._find_iscsi_ips_from_storage(
host_name)

return self._construct_properties(multipath, target_lun,
target_ips, target_iqns)
Expand Down
6 changes: 6 additions & 0 deletions Cinder/Stein/fs_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,3 +752,9 @@ def get_iscsi_info_from_conf(manager_groups, iscsi_manager_groups, use_ipv6,
break

return node_ips, target_ips, target_iqns


def get_iscsi_info_from_storage(manager_ips, use_ipv6, valid_iscsi_ips,
valid_node_ips):
return _get_target_info(manager_ips, use_ipv6, valid_iscsi_ips,
valid_node_ips)
7 changes: 7 additions & 0 deletions Cinder/Train/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@
CONF_POOLS = "dsware_storage_pools"
CONF_PWD = "san_password"
CONF_USER = "san_login"
CONF_IP = "san_ip"
CONF_PORT = "san_port"
CONF_NEW_POOLS = "storage_pools"
CONF_STORAGE_CA_FILEPATH = "storage_ca_filepath"
CONF_STORAGE_KEY_FILEPATH = "storage_key_filepath"
CONF_STORAGE_CERT_FILEPATH = "storage_cert_filepath"
CONF_STORAGE_SSL_TWO_WAY_AUTH = "storage_ssl_two_way_auth"

QOS_MUST_SET = ["maxIOPS", "maxMBPS"]
QOS_KEYS = ["maxIOPS", "maxMBPS", "total_iops_sec", "total_bytes_sec"]
Expand Down
Loading

0 comments on commit fd0449f

Please sign in to comment.