</config>
"""
+HNAS_WRONG_CONF3 = """<?xml version="1.0" encoding="UTF-8" ?>
+<config>
+ <hnas_cmd>ssc</hnas_cmd>
+ <mgmt_ip0>172.17.44.15</mgmt_ip0>
+ <username> </username>
+ <password>supervisor</password>
+ <svc_0>
+ <volume_type>default</volume_type>
+ <hdp>172.17.39.132:/cinder</hdp>
+ </svc_0>
+ <svc_1>
+ <volume_type>silver</volume_type>
+ <hdp>172.17.39.133:/cinder</hdp>
+ </svc_1>
+</config>
+"""
+
+HNAS_WRONG_CONF4 = """<?xml version="1.0" encoding="UTF-8" ?>
+<config>
+ <hnas_cmd>ssc</hnas_cmd>
+ <mgmt_ip0>172.17.44.15</mgmt_ip0>
+ <username>super</username>
+ <password>supervisor</password>
+ <svc_0>
+ <volume_type>default</volume_type>
+ <hdp>172.17.39.132:/cinder</hdp>
+ </svc_0>
+ <svc_4>
+ <volume_type>silver</volume_type>
+ <hdp>172.17.39.133:/cinder</hdp>
+ </svc_4>
+</config>
+"""
+
+HNAS_FULL_CONF = """<?xml version="1.0" encoding="UTF-8" ?>
+<config>
+ <hnas_cmd>ssc</hnas_cmd>
+ <mgmt_ip0>172.17.44.15</mgmt_ip0>
+ <username>super</username>
+ <password>supervisor</password>
+ <ssh_enabled>True</ssh_enabled>
+ <ssh_port>2222</ssh_port>
+ <chap_enabled>True</chap_enabled>
+ <ssh_private_key>/etc/cinder/ssh_priv</ssh_private_key>
+ <cluster_admin_ip0>10.0.0.1</cluster_admin_ip0>
+ <svc_0>
+ <volume_type>default</volume_type>
+ <hdp>172.17.39.132:/cinder</hdp>
+ </svc_0>
+ <svc_1>
+ <volume_type>silver</volume_type>
+ <hdp>172.17.39.133:/cinder/silver </hdp>
+ </svc_1>
+ <svc_2>
+ <volume_type>gold</volume_type>
+ <hdp>172.17.39.133:/cinder/gold</hdp>
+ </svc_2>
+ <svc_3>
+ <volume_type>platinum</volume_type>
+ <hdp>172.17.39.133:/cinder/platinum</hdp>
+ </svc_3>
+</config>
+"""
+
+
# The following information is passed on to tests, when creating a volume
_SERVICE = ('Test_hdp', 'Test_path', 'Test_label')
_SHARE = '172.17.39.132:/cinder'
self.configuration.hds_hnas_iscsi_config_file = ''
self.assertRaises(exception.ParameterNotFound, nfs._read_config, '')
+ # Test exception when config file has parsing errors
+ # due to blank tag
+ m_open.return_value = six.StringIO(HNAS_WRONG_CONF3)
+ self.configuration.hds_hnas_iscsi_config_file = ''
+ self.assertRaises(exception.ParameterNotFound, nfs._read_config, '')
+
+ # Test when config file has parsing errors due invalid svc_number
+ m_open.return_value = six.StringIO(HNAS_WRONG_CONF4)
+ self.configuration.hds_hnas_iscsi_config_file = ''
+ config = nfs._read_config('')
+ self.assertEqual(1, len(config['services']))
+
+ # Test config with full options
+ # due invalid svc_number
+ m_open.return_value = six.StringIO(HNAS_FULL_CONF)
+ self.configuration.hds_hnas_iscsi_config_file = ''
+ config = nfs._read_config('')
+ self.assertEqual(4, len(config['services']))
+
@mock.patch.object(nfs.HDSNFSDriver, '_id_to_vol')
@mock.patch.object(nfs.HDSNFSDriver, '_get_provider_location')
@mock.patch.object(nfs.HDSNFSDriver, '_get_export_path')
iSCSI Cinder Volume driver for Hitachi Unified Storage (HUS-HNAS) platform.
"""
import os
+import re
import six
from xml.etree import ElementTree as ETree
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
-from oslo_utils import excutils
from oslo_utils import units
from cinder.volume import utils
from cinder.volume import volume_types
-HDS_HNAS_ISCSI_VERSION = '4.0.0'
+HDS_HNAS_ISCSI_VERSION = '4.1.0'
LOG = logging.getLogger(__name__)
def _xml_read(root, element, check=None):
"""Read an xml element."""
- try:
- val = root.findtext(element)
- LOG.info(_LI("%(element)s: %(val)s"),
- {'element': element,
- 'val': val if element != 'password' else '***'})
- if val:
- return val.strip()
- if check:
- raise exception.ParameterNotFound(param=element)
+ val = root.findtext(element)
+
+ # mandatory parameter not found
+ if val is None and check:
+ raise exception.ParameterNotFound(param=element)
+
+ # tag not found
+ if val is None:
return None
- except ETree.ParseError:
- if check:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("XML exception reading parameter: %s"), element)
+
+ svc_tag_pattern = re.compile("svc_[0-3]$")
+ # tag found but empty parameter.
+ if not val.strip():
+ # Service tags are empty
+ if svc_tag_pattern.search(element):
+ return ""
else:
- LOG.info(_LI("XML exception reading parameter: %s"), element)
- return None
+ raise exception.ParameterNotFound(param=element)
+
+ LOG.debug(_LI("%(element)s: %(val)s"),
+ {'element': element,
+ 'val': val if element != 'password' else '***'})
+
+ return val.strip()
def _read_config(xml_config_file):
config = {}
arg_prereqs = ['mgmt_ip0', 'username']
for req in arg_prereqs:
- config[req] = _xml_read(root, req, 'check')
+ config[req] = _xml_read(root, req, True)
# optional parameters
opt_parameters = ['hnas_cmd', 'ssh_enabled', 'chap_enabled',
config['chap_enabled'] = HNAS_DEFAULT_CONFIG['chap_enabled']
if config['ssh_enabled'] == 'True':
- config['ssh_private_key'] = _xml_read(root, 'ssh_private_key', 'check')
+ config['ssh_private_key'] = _xml_read(root, 'ssh_private_key', True)
config['ssh_port'] = _xml_read(root, 'ssh_port')
config['password'] = _xml_read(root, 'password')
if config['ssh_port'] is None:
config['ssh_port'] = HNAS_DEFAULT_CONFIG['ssh_port']
else:
# password is mandatory when not using SSH
- config['password'] = _xml_read(root, 'password', 'check')
+ config['password'] = _xml_read(root, 'password', True)
if config['hnas_cmd'] is None:
config['hnas_cmd'] = HNAS_DEFAULT_CONFIG['hnas_cmd']
# none optional
for arg in ['volume_type', 'hdp', 'iscsi_ip']:
- service[arg] = _xml_read(root, svc + '/' + arg, 'check')
+ service[arg] = _xml_read(root, svc + '/' + arg, True)
config['services'][service['volume_type']] = service
config['hdp'][service['hdp']] = service['hdp']
Fixed concurrency errors
Version 3.3.0: Fixed iSCSI target limitation error
Version 4.0.0: Added manage/unmanage features
+ Version 4.1.0: Fixed XML parser checks on blank options
"""
def __init__(self, *args, **kwargs):
import math
import os
+import re
import six
import socket
import time
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
-from oslo_utils import excutils
from oslo_utils import units
from cinder import exception
from cinder.volume import volume_types
-HDS_HNAS_NFS_VERSION = '4.0.0'
+HDS_HNAS_NFS_VERSION = '4.1.0'
LOG = logging.getLogger(__name__)
def _xml_read(root, element, check=None):
- """Read an xml element.
+ """Read an xml element."""
- :param root: XML object
- :param element: string desired tag
- :param check: string if present, throw exception if element missing
- """
+ val = root.findtext(element)
- try:
- val = root.findtext(element)
- LOG.info(_LI("%(element)s: %(val)s"),
- {'element': element,
- 'val': val if element != 'password' else '***'})
- if val:
- return val.strip()
- if check:
- raise exception.ParameterNotFound(param=element)
+ # mandatory parameter not found
+ if val is None and check:
+ raise exception.ParameterNotFound(param=element)
+
+ # tag not found
+ if val is None:
return None
- except ETree.ParseError:
- if check:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("XML exception reading parameter: %s"), element)
- else:
- LOG.info(_LI("XML exception reading parameter: %s"), element)
- return None
+
+ svc_tag_pattern = re.compile("svc_.$")
+ # tag found but empty parameter.
+ if not val.strip():
+ if svc_tag_pattern.search(element):
+ return ""
+ raise exception.ParameterNotFound(param=element)
+
+ LOG.debug(_LI("%(element)s: %(val)s"),
+ {'element': element,
+ 'val': val if element != 'password' else '***'})
+
+ return val.strip()
def _read_config(xml_config_file):
config = {}
arg_prereqs = ['mgmt_ip0', 'username']
for req in arg_prereqs:
- config[req] = _xml_read(root, req, 'check')
+ config[req] = _xml_read(root, req, True)
# optional parameters
opt_parameters = ['hnas_cmd', 'ssh_enabled', 'cluster_admin_ip0']
config[req] = _xml_read(root, req)
if config['ssh_enabled'] == 'True':
- config['ssh_private_key'] = _xml_read(root, 'ssh_private_key', 'check')
+ config['ssh_private_key'] = _xml_read(root, 'ssh_private_key', True)
config['password'] = _xml_read(root, 'password')
config['ssh_port'] = _xml_read(root, 'ssh_port')
if config['ssh_port'] is None:
config['ssh_port'] = HNAS_DEFAULT_CONFIG['ssh_port']
else:
# password is mandatory when not using SSH
- config['password'] = _xml_read(root, 'password', 'check')
+ config['password'] = _xml_read(root, 'password', True)
if config['hnas_cmd'] is None:
config['hnas_cmd'] = HNAS_DEFAULT_CONFIG['hnas_cmd']
# none optional
for arg in ['volume_type', 'hdp']:
- service[arg] = _xml_read(root, svc + '/' + arg, 'check')
+ service[arg] = _xml_read(root, svc + '/' + arg, True)
config['services'][service['volume_type']] = service
config['hdp'][service['hdp']] = service['hdp']
Version 2.2.0: Added support to SSH authentication
Version 3.0.0: Added pool aware scheduling
Version 4.0.0: Added manage/unmanage features
+ Version 4.1.0: Fixed XML parser checks on blank options
"""
def __init__(self, *args, **kwargs):