HNAS: Deprecating XML config file

Currently HNAS drivers use an external XML configuration file.
This patch will deprecate this file and get all the configurations
from cinder.conf.

Co-Authored-By: Alyson Rosa <alyson.rodrigues.rosa@gmail.com>

DocImpact
Depends-On: I1b5d5155306c39c528af4037a5f54cf3d4dc1ffa
Change-Id: I00f4ae5ccd99f8f5da12e687ef0dc9cdb1717b3b
This commit is contained in:
Adriano Rosso 2016-02-25 17:14:57 -03:00
parent 387e543475
commit 3f292f024e
9 changed files with 368 additions and 66 deletions

View File

@ -109,6 +109,8 @@ from cinder.volume.drivers.hitachi import hnas_iscsi as \
cinder_volume_drivers_hitachi_hnasiscsi
from cinder.volume.drivers.hitachi import hnas_nfs as \
cinder_volume_drivers_hitachi_hnasnfs
from cinder.volume.drivers.hitachi import hnas_utils as \
cinder_volume_drivers_hitachi_hnasutils
from cinder.volume.drivers.hpe import hpe_3par_common as \
cinder_volume_drivers_hpe_hpe3parcommon
from cinder.volume.drivers.hpe import hpe_lefthand_iscsi as \
@ -208,6 +210,7 @@ def list_opts():
itertools.chain(
cinder_backup_driver.service_opts,
[cinder_cmd_volume.cluster_opt],
cinder_volume_drivers_hitachi_hnasutils.drivers_common_opts,
cinder_api_common.api_common_opts,
cinder_backup_drivers_ceph.service_opts,
cinder_volume_drivers_smbfs.volume_opts,

View File

@ -71,7 +71,7 @@ class HNASiSCSIDriverTest(test.TestCase):
self.context, **_VOLUME)
self.volume_clone = fake_volume.fake_volume_obj(
self.context, **_VOLUME2)
self.snapshot = self.snapshot = self.instantiate_snapshot(_SNAPSHOT)
self.snapshot = self.instantiate_snapshot(_SNAPSHOT)
self.volume_type = fake_volume.fake_volume_type_obj(
None,
@ -115,7 +115,7 @@ class HNASiSCSIDriverTest(test.TestCase):
},
'cluster_admin_ip0': None,
'ssh_private_key': None,
'chap_enabled': 'True',
'chap_enabled': True,
'mgmt_ip0': '172.17.44.15',
'ssh_enabled': None
}
@ -123,7 +123,7 @@ class HNASiSCSIDriverTest(test.TestCase):
self.configuration = mock.Mock(spec=conf.Configuration)
self.configuration.hds_hnas_iscsi_config_file = 'fake.xml'
self.mock_object(hnas_utils, 'read_config',
self.mock_object(hnas_utils, 'read_cinder_conf',
mock.Mock(return_value=self.parsed_xml))
self.driver = iscsi.HNASISCSIDriver(configuration=self.configuration)
@ -189,7 +189,7 @@ class HNASiSCSIDriverTest(test.TestCase):
'auth': 'Enabled'}}
iqn = 'iqn.2014-12.10.10.10.10:evstest1.cinder-default'
self.driver.config['chap_enabled'] = 'False'
self.driver.config['chap_enabled'] = False
self.mock_object(HNASSSHBackend, 'get_evs',
mock.Mock(return_value='1'))

View File

@ -119,7 +119,7 @@ class HNASNFSDriverTest(test.TestCase):
self.configuration = mock.Mock(spec=conf.Configuration)
self.configuration.hds_hnas_nfs_config_file = 'fake.xml'
self.mock_object(hnas_utils, 'read_config',
self.mock_object(hnas_utils, 'read_cinder_conf',
mock.Mock(return_value=self.parsed_xml))
self.configuration = mock.Mock(spec=conf.Configuration)

View File

@ -14,6 +14,8 @@
# under the License.
#
import copy
import ddt
import mock
import os
@ -25,10 +27,11 @@ from cinder import exception
from cinder import test
from cinder.tests.unit import fake_constants
from cinder.tests.unit import fake_volume
from cinder.volume import configuration as conf
from cinder.volume.drivers.hitachi import hnas_iscsi
from cinder.volume.drivers.hitachi import hnas_utils
from cinder.volume import volume_types
_VOLUME = {'name': 'cinder-volume',
'id': fake_constants.VOLUME_ID,
'size': 128,
@ -37,17 +40,18 @@ _VOLUME = {'name': 'cinder-volume',
'provider_location': 'hnas'}
service_parameters = ['volume_type', 'hdp']
optional_parameters = ['hnas_cmd', 'cluster_admin_ip0', 'iscsi_ip']
optional_parameters = ['ssc_cmd', 'cluster_admin_ip0', 'iscsi_ip']
config_from_cinder_conf = {
'username': 'supervisor',
'fs': {'silver': 'silver',
'easy-stack': 'easy-stack'},
'ssh_port': '22',
'chap_enabled': None,
'fs': {'easy-stack': 'easy-stack',
'silver': 'silver'},
'ssh_port': 22,
'chap_enabled': True,
'cluster_admin_ip0': None,
'ssh_private_key': None,
'mgmt_ip0': '172.24.44.15',
'ssc_cmd': 'ssc',
'services': {
'default': {
'label': u'svc_0',
@ -57,8 +61,7 @@ config_from_cinder_conf = {
'label': u'svc_1',
'volume_type': 'FS-CinderDev1',
'hdp': 'silver'}},
'password': 'supervisor',
'hnas_cmd': 'ssc'}
'password': 'supervisor'}
valid_XML_str = '''
<config>
@ -122,13 +125,14 @@ XML_no_services_configured = '''
<mgmt_ip0>172.24.44.15</mgmt_ip0>
<username>supervisor</username>
<password>supervisor</password>
<ssh_port>10</ssh_port>
<ssh_enabled>False</ssh_enabled>
<ssh_private_key>/home/ubuntu/.ssh/id_rsa</ssh_private_key>
</config>
'''
parsed_xml = {'username': 'supervisor', 'password': 'supervisor',
'hnas_cmd': 'ssc', 'iscsi_ip': None, 'ssh_port': '22',
'ssc_cmd': 'ssc', 'iscsi_ip': None, 'ssh_port': 22,
'fs': {'easy-stack': 'easy-stack',
'FS-CinderDev1': 'FS-CinderDev1'},
'cluster_admin_ip0': None,
@ -149,6 +153,7 @@ invalid_XML_etree_no_service = ETree.XML(XML_no_services_configured)
CONF = cfg.CONF
@ddt.ddt
class HNASUtilsTest(test.TestCase):
def __init__(self, *args, **kwargs):
@ -156,13 +161,26 @@ class HNASUtilsTest(test.TestCase):
def setUp(self):
super(HNASUtilsTest, self).setUp()
self.fake_conf = conf.Configuration(hnas_utils.CONF)
self.fake_conf.append_config_values(hnas_iscsi.iSCSI_OPTS)
self.override_config('hnas_username', 'supervisor')
self.override_config('hnas_password', 'supervisor')
self.override_config('hnas_mgmt_ip0', '172.24.44.15')
self.override_config('hnas_svc0_volume_type', 'default')
self.override_config('hnas_svc0_hdp', 'easy-stack')
self.override_config('hnas_svc0_iscsi_ip', '172.24.49.21')
self.override_config('hnas_svc1_volume_type', 'FS-CinderDev1')
self.override_config('hnas_svc1_hdp', 'silver')
self.override_config('hnas_svc1_iscsi_ip', '172.24.49.32')
self.context = context.get_admin_context()
self.volume = fake_volume.fake_volume_obj(self.context, **_VOLUME)
self.volume_type = (fake_volume.fake_volume_type_obj(None, **{
'id': fake_constants.VOLUME_TYPE_ID, 'name': 'silver'}))
def test_read_config(self):
def test_read_xml_config(self):
self.mock_object(os, 'access', mock.Mock(return_value=True))
self.mock_object(ETree, 'parse',
mock.Mock(return_value=ETree.ElementTree))
@ -170,29 +188,29 @@ class HNASUtilsTest(test.TestCase):
mock.Mock(return_value=valid_XML_etree))
xml_path = 'xml_file_found'
out = hnas_utils.read_config(xml_path,
service_parameters,
optional_parameters)
out = hnas_utils.read_xml_config(xml_path,
service_parameters,
optional_parameters)
self.assertEqual(parsed_xml, out)
def test_read_config_parser_error(self):
def test_read_xml_config_parser_error(self):
xml_file = 'hnas_nfs.xml'
self.mock_object(os, 'access', mock.Mock(return_value=True))
self.mock_object(ETree, 'parse',
mock.Mock(side_effect=ETree.ParseError))
self.assertRaises(exception.ConfigNotFound, hnas_utils.read_config,
self.assertRaises(exception.ConfigNotFound, hnas_utils.read_xml_config,
xml_file, service_parameters, optional_parameters)
def test_read_config_not_found(self):
def test_read_xml_config_not_found(self):
self.mock_object(os, 'access', mock.Mock(return_value=False))
xml_path = 'xml_file_not_found'
self.assertRaises(exception.NotFound, hnas_utils.read_config,
self.assertRaises(exception.NotFound, hnas_utils.read_xml_config,
xml_path, service_parameters, optional_parameters)
def test_read_config_without_services_configured(self):
def test_read_xml_config_without_services_configured(self):
xml_file = 'hnas_nfs.xml'
self.mock_object(os, 'access', mock.Mock(return_value=True))
@ -201,10 +219,11 @@ class HNASUtilsTest(test.TestCase):
self.mock_object(ETree.ElementTree, 'getroot',
mock.Mock(return_value=invalid_XML_etree_no_service))
self.assertRaises(exception.ParameterNotFound, hnas_utils.read_config,
xml_file, service_parameters, optional_parameters)
self.assertRaises(exception.ParameterNotFound,
hnas_utils.read_xml_config, xml_file,
service_parameters, optional_parameters)
def test_read_config_empty_authentication_parameter(self):
def test_read_xml_config_empty_authentication_parameter(self):
xml_file = 'hnas_nfs.xml'
self.mock_object(os, 'access', mock.Mock(return_value=True))
@ -214,10 +233,11 @@ class HNASUtilsTest(test.TestCase):
mock.Mock(return_value=
invalid_XML_etree_empty_parameter))
self.assertRaises(exception.ParameterNotFound, hnas_utils.read_config,
xml_file, service_parameters, optional_parameters)
self.assertRaises(exception.ParameterNotFound,
hnas_utils.read_xml_config, xml_file,
service_parameters, optional_parameters)
def test_read_config_mandatory_parameters_missing(self):
def test_read_xml_config_mandatory_parameters_missing(self):
xml_file = 'hnas_nfs.xml'
self.mock_object(os, 'access', mock.Mock(return_value=True))
@ -227,10 +247,11 @@ class HNASUtilsTest(test.TestCase):
mock.Mock(return_value=
invalid_XML_etree_no_mandatory_params))
self.assertRaises(exception.ParameterNotFound, hnas_utils.read_config,
xml_file, service_parameters, optional_parameters)
self.assertRaises(exception.ParameterNotFound,
hnas_utils.read_xml_config, xml_file,
service_parameters, optional_parameters)
def test_read_config_XML_without_authentication_parameter(self):
def test_read_config_xml_without_authentication_parameter(self):
xml_file = 'hnas_nfs.xml'
self.mock_object(os, 'access', mock.Mock(return_value=True))
@ -240,7 +261,7 @@ class HNASUtilsTest(test.TestCase):
mock.Mock(return_value=
invalid_XML_etree_no_authentication))
self.assertRaises(exception.ConfigNotFound, hnas_utils.read_config,
self.assertRaises(exception.ConfigNotFound, hnas_utils.read_xml_config,
xml_file, service_parameters, optional_parameters)
def test_get_pool_with_vol_type(self):
@ -254,6 +275,56 @@ class HNASUtilsTest(test.TestCase):
self.assertEqual('silver', out)
def test_get_pool_with_vol_type_id_none(self):
self.volume.volume_type_id = None
self.volume.volume_type = self.volume_type
out = hnas_utils.get_pool(parsed_xml, self.volume)
self.assertEqual('default', out)
def test_get_pool_with_missing_service_label(self):
self.mock_object(volume_types, 'get_volume_type_extra_specs',
mock.Mock(return_value={'service_label': 'gold'}))
self.volume.volume_type_id = fake_constants.VOLUME_TYPE_ID
self.volume.volume_type = self.volume_type
out = hnas_utils.get_pool(parsed_xml, self.volume)
self.assertEqual('default', out)
def test_get_pool_without_vol_type(self):
out = hnas_utils.get_pool(parsed_xml, self.volume)
self.assertEqual('default', out)
def test_read_cinder_conf_nfs(self):
out = hnas_utils.read_cinder_conf(self.fake_conf, 'nfs')
self.assertEqual(config_from_cinder_conf, out)
def test_read_cinder_conf_iscsi(self):
local_config = copy.deepcopy(config_from_cinder_conf)
local_config['services']['FS-CinderDev1']['iscsi_ip'] = '172.24.49.32'
local_config['services']['default']['iscsi_ip'] = '172.24.49.21'
out = hnas_utils.read_cinder_conf(self.fake_conf, 'iscsi')
self.assertEqual(local_config, out)
def test_read_cinder_conf_break(self):
self.override_config('hnas_username', None)
self.override_config('hnas_password', None)
self.override_config('hnas_mgmt_ip0', None)
out = hnas_utils.read_cinder_conf(self.fake_conf, 'nfs')
self.assertIsNone(out)
@ddt.data('hnas_username', 'hnas_password',
'hnas_mgmt_ip0', 'hnas_svc0_iscsi_ip', 'hnas_svc0_volume_type',
'hnas_svc0_hdp', )
def test_init_invalid_conf_parameters(self, attr_name):
self.override_config(attr_name, None)
self.assertRaises(exception.InvalidParameterValue,
hnas_utils.read_cinder_conf, self.fake_conf, 'iscsi')

View File

@ -36,7 +36,7 @@ class HNASSSHBackend(object):
def __init__(self, backend_opts):
self.mgmt_ip0 = backend_opts.get('mgmt_ip0')
self.hnas_cmd = backend_opts.get('hnas_cmd', 'ssc')
self.hnas_cmd = backend_opts.get('ssc_cmd', 'ssc')
self.cluster_admin_ip0 = backend_opts.get('cluster_admin_ip0')
self.ssh_port = backend_opts.get('ssh_port', '22')
self.ssh_username = backend_opts.get('username')

View File

@ -41,14 +41,31 @@ LOG = logging.getLogger(__name__)
iSCSI_OPTS = [
cfg.StrOpt('hds_hnas_iscsi_config_file',
default='/opt/hds/hnas/cinder_iscsi_conf.xml',
help='Configuration file for HNAS iSCSI cinder plugin')]
help='Legacy configuration file for HNAS iSCSI Cinder '
'plugin. This is not needed if you fill all '
'configuration on cinder.conf',
deprecated_for_removal=True),
cfg.BoolOpt('hnas_chap_enabled',
default=True,
help='Whether the chap authentication is enabled in the '
'iSCSI target or not.'),
cfg.IPOpt('hnas_svc0_iscsi_ip',
help='Service 0 iSCSI IP'),
cfg.IPOpt('hnas_svc1_iscsi_ip',
help='Service 1 iSCSI IP'),
cfg.IPOpt('hnas_svc2_iscsi_ip',
help='Service 2 iSCSI IP'),
cfg.IPOpt('hnas_svc3_iscsi_ip',
help='Service 3 iSCSI IP')
]
CONF = cfg.CONF
CONF.register_opts(iSCSI_OPTS)
HNAS_DEFAULT_CONFIG = {'hnas_cmd': 'ssc',
'chap_enabled': 'True',
'ssh_port': '22'}
HNAS_DEFAULT_CONFIG = {'ssc_cmd': 'ssc',
'chap_enabled': True,
'ssh_port': 22}
MAX_HNAS_ISCSI_TARGETS = 32
MAX_HNAS_LUS_PER_TARGET = 32
@ -75,7 +92,8 @@ class HNASISCSIDriver(driver.ISCSIDriver):
Removed the option to use local SSC (ssh_enabled=False)
Updated to use versioned objects
Changed the class name to HNASISCSIDriver
"""
Deprecated XML config file
"""
# ThirdPartySystems wiki page
CI_WIKI_NAME = "Hitachi_HNAS_CI"
@ -83,18 +101,29 @@ class HNASISCSIDriver(driver.ISCSIDriver):
def __init__(self, *args, **kwargs):
"""Initializes and reads different config parameters."""
self.configuration = kwargs.get('configuration', None)
self.context = {}
self.config = {}
service_parameters = ['volume_type', 'hdp', 'iscsi_ip']
optional_parameters = ['hnas_cmd', 'cluster_admin_ip0',
optional_parameters = ['ssc_cmd', 'cluster_admin_ip0',
'chap_enabled']
if self.configuration:
self.configuration.append_config_values(
hnas_utils.drivers_common_opts)
self.configuration.append_config_values(iSCSI_OPTS)
self.config = hnas_utils.read_config(
self.configuration.hds_hnas_iscsi_config_file,
service_parameters,
optional_parameters)
# Trying to get HNAS configuration from cinder.conf
self.config = hnas_utils.read_cinder_conf(
self.configuration, 'iscsi')
# If HNAS configuration are not set on cinder.conf, tries to use
# the deprecated XML configuration file
if not self.config:
self.config = hnas_utils.read_xml_config(
self.configuration.hds_hnas_iscsi_config_file,
service_parameters,
optional_parameters)
super(HNASISCSIDriver, self).__init__(*args, **kwargs)
self.backend = hnas_backend.HNASSSHBackend(self.config)
@ -185,7 +214,7 @@ class HNASISCSIDriver(driver.ISCSIDriver):
# see if the client supports CHAP authentication and if
# iscsi_secret has already been set, retrieve the secret if
# available, otherwise generate and store
if self.config['chap_enabled'] == 'True':
if self.config['chap_enabled']:
# CHAP support is enabled. Tries to get the target secret.
if 'iscsi_secret' not in tgt_info.keys():
LOG.info(_LI("Retrieving secret for service: %(tgt)s."),
@ -217,7 +246,7 @@ class HNASISCSIDriver(driver.ISCSIDriver):
self.backend.create_target(tgt_alias, fs_label,
tgt_info['iscsi_secret'])
elif (tgt['tgt']['secret'] == "" and
self.config['chap_enabled'] == 'True'):
self.config['chap_enabled']):
# The target exists, has no secret and chap is enabled
self.backend.set_target_secret(tgt_alias, fs_label,
tgt_info['iscsi_secret'])
@ -482,7 +511,7 @@ class HNASISCSIDriver(driver.ISCSIDriver):
properties['volume_id'] = volume.id
properties['auth_username'] = connector['initiator']
if self.config['chap_enabled'] == 'True':
if self.config['chap_enabled']:
properties['auth_method'] = 'CHAP'
properties['auth_password'] = secret

View File

@ -45,12 +45,16 @@ LOG = logging.getLogger(__name__)
NFS_OPTS = [
cfg.StrOpt('hds_hnas_nfs_config_file',
default='/opt/hds/hnas/cinder_nfs_conf.xml',
help='Configuration file for HNAS NFS cinder plugin'), ]
help='Legacy configuration file for HNAS NFS Cinder plugin. '
'This is not needed if you fill all configuration on '
'cinder.conf',
deprecated_for_removal=True)
]
CONF = cfg.CONF
CONF.register_opts(NFS_OPTS)
HNAS_DEFAULT_CONFIG = {'hnas_cmd': 'ssc', 'ssh_port': '22'}
HNAS_DEFAULT_CONFIG = {'ssc_cmd': 'ssc', 'ssh_port': '22'}
@interface.volumedriver
@ -74,6 +78,7 @@ class HNASNFSDriver(nfs.NfsDriver):
Removed the option to use local SSC (ssh_enabled=False)
Updated to use versioned objects
Changed the class name to HNASNFSDriver
Deprecated XML config file
"""
# ThirdPartySystems wiki page
CI_WIKI_NAME = "Hitachi_HNAS_CI"
@ -84,14 +89,25 @@ class HNASNFSDriver(nfs.NfsDriver):
self.configuration = kwargs.get('configuration', None)
service_parameters = ['volume_type', 'hdp']
optional_parameters = ['hnas_cmd', 'cluster_admin_ip0']
optional_parameters = ['ssc_cmd', 'cluster_admin_ip0']
if self.configuration:
self.configuration.append_config_values(
hnas_utils.drivers_common_opts)
self.configuration.append_config_values(NFS_OPTS)
self.config = hnas_utils.read_config(
self.configuration.hds_hnas_nfs_config_file,
service_parameters,
optional_parameters)
self.config = {}
# Trying to get HNAS configuration from cinder.conf
self.config = hnas_utils.read_cinder_conf(
self.configuration, 'nfs')
# If HNAS configuration are not set on cinder.conf, tries to use
# the deprecated XML configuration file
if not self.config:
self.config = hnas_utils.read_xml_config(
self.configuration.hds_hnas_nfs_config_file,
service_parameters,
optional_parameters)
super(HNASNFSDriver, self).__init__(*args, **kwargs)
self.backend = hnas_backend.HNASSSHBackend(self.config)

View File

@ -20,21 +20,120 @@ Shared code for HNAS drivers
import os
import re
from oslo_config import cfg
from oslo_log import log as logging
import six
from xml.etree import ElementTree as ETree
from cinder import exception
from cinder.i18n import _
from cinder.i18n import _, _LW
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
HNAS_DEFAULT_CONFIG = {'hnas_cmd': 'ssc',
'chap_enabled': 'True',
'ssh_port': '22'}
HNAS_DEFAULT_CONFIG = {'ssc_cmd': 'ssc',
'chap_enabled': True,
'ssh_port': 22}
MAX_HNAS_ISCSI_TARGETS = 32
drivers_common_opts = [
cfg.IPOpt('hnas_mgmt_ip0',
help='Management IP address of HNAS. This can '
'be any IP in the admin address on HNAS or '
'the SMU IP.'),
cfg.StrOpt('hnas_ssc_cmd',
default='ssc',
help='Command to communicate to HNAS array.'),
cfg.StrOpt('hnas_username',
help='HNAS username.'),
cfg.StrOpt('hnas_password',
secret=True,
help='HNAS password.'),
cfg.PortOpt('hnas_ssh_port',
default=22,
help='Port to be used for SSH authentication.'),
cfg.StrOpt('hnas_ssh_private_key',
help='Path to the SSH private key used to '
'authenticate in HNAS SMU.'),
cfg.StrOpt('hnas_cluster_admin_ip0',
default=None,
help='The IP of the HNAS cluster admin. '
'Required only for HNAS multi-cluster setups.'),
cfg.StrOpt('hnas_svc0_volume_type',
help='Service 0 volume type'),
cfg.StrOpt('hnas_svc0_hdp',
help='Service 0 HDP'),
cfg.StrOpt('hnas_svc1_volume_type',
help='Service 1 volume type'),
cfg.StrOpt('hnas_svc1_hdp',
help='Service 1 HDP'),
cfg.StrOpt('hnas_svc2_volume_type',
help='Service 2 volume type'),
cfg.StrOpt('hnas_svc2_hdp',
help='Service 2 HDP'),
cfg.StrOpt('hnas_svc3_volume_type',
help='Service 3 volume type'),
cfg.StrOpt('hnas_svc3_hdp',
help='Service 3 HDP')
]
CONF = cfg.CONF
CONF.register_opts(drivers_common_opts)
def _check_conf_params(config, vol_type, dv_type, idx):
"""Validates if the configuration on cinder.conf is complete.
:param config: Dictionary with the driver configurations
:param vol_type: The volume type of the current pool
:param dv_type: The type of the driver (NFS or iSCSI)
:param idx: Index of the current pool
"""
# Validating the inputs on cinder.conf
if config['username'] is None:
msg = (_("The config parameter hnas_username "
"is not set in the cinder.conf."))
raise exception.InvalidParameterValue(err=msg)
if (config['password'] is None and
config['ssh_private_key'] is None):
msg = (_("Credentials configuration parameters "
"missing: you need to set hnas_password "
"or hnas_ssh_private_key "
"in the cinder.conf."))
raise exception.InvalidParameterValue(err=msg)
if config['mgmt_ip0'] is None:
msg = (_("The config parameter hnas_mgmt_ip0 "
"is not set in the cinder.conf."))
raise exception.InvalidParameterValue(err=msg)
if config['services'][vol_type]['hdp'] is None:
msg = (_("The config parameter hnas_svc%(idx)s_hdp is "
"not set in the cinder.conf. Note that you need to "
"have at least one pool configured.") %
{'idx': idx})
raise exception.InvalidParameterValue(err=msg)
if config['services'][vol_type]['volume_type'] is None:
msg = (_("The config parameter "
"hnas_svc%(idx)s_volume_type is not set "
"in the cinder.conf. Note that you need to "
"have at least one pool configured.") %
{'idx': idx})
raise exception.InvalidParameterValue(err=msg)
if (dv_type == 'iscsi' and
config['services'][vol_type]['iscsi_ip'] is None):
msg = (_("The config parameter "
"hnas_svc%(idx)s_iscsi_ip is not set "
"in the cinder.conf. Note that you need to "
"have at least one pool configured.") % {'idx': idx})
raise exception.InvalidParameterValue(err=msg)
def _xml_read(root, element, check=None):
"""Read an xml element.
@ -68,19 +167,26 @@ def _xml_read(root, element, check=None):
return val.strip()
def read_config(xml_config_file, svc_params, optional_params):
def read_xml_config(xml_config_file, svc_params, optional_params):
"""Read Hitachi driver specific xml config file.
:param xml_config_file: string filename containing XML configuration
:param svc_params: parameters to configure the services
['volume_type', 'hdp', 'iscsi_ip']
:param optional_params: parameters to configure that are not mandatory
['hnas_cmd', 'ssh_enabled', 'cluster_admin_ip0', 'chap_enabled']
['ssc_cmd', 'cluster_admin_ip0', 'chap_enabled']
"""
if not os.access(xml_config_file, os.R_OK):
msg = (_("Can't open config file: %s") % xml_config_file)
raise exception.NotFound(message=msg)
msg = (_("Can't find HNAS configurations on cinder.conf neither "
"on the path %(xml)s.") % {'xml': xml_config_file})
raise exception.ConfigNotFound(message=msg)
else:
LOG.warning(_LW("This XML configuration file %(xml)s is deprecated. "
"Please, move all the configurations to the "
"cinder.conf file. If you keep both configuration "
"files, the options set on cinder.conf will be "
"used."), {'xml': xml_config_file})
try:
root = ETree.parse(xml_config_file).getroot()
@ -107,8 +213,9 @@ def read_config(xml_config_file, svc_params, optional_params):
msg = (_("Missing authentication option (passw or private key file)."))
raise exception.ConfigNotFound(message=msg)
config['ssh_port'] = _xml_read(root, 'ssh_port')
if config['ssh_port'] is None:
if _xml_read(root, 'ssh_port') is not None:
config['ssh_port'] = int(_xml_read(root, 'ssh_port'))
else:
config['ssh_port'] = HNAS_DEFAULT_CONFIG['ssh_port']
config['fs'] = {}
@ -141,6 +248,7 @@ def get_pool(config, volume):
:param volume: dictionary volume reference
:returns: the pool related to the volume
"""
if volume.volume_type:
metadata = {}
type_id = volume.volume_type_id
@ -150,3 +258,71 @@ def get_pool(config, volume):
if metadata['service_label'] in config['services'].keys():
return metadata['service_label']
return 'default'
def read_cinder_conf(config_opts, dv_type):
"""Reads cinder.conf
Gets the driver specific information set on cinder.conf configuration
file.
:param config_opts: Configuration object that contains the information
needed by HNAS driver
:param dv_type: The type of the driver (NFS or iSCSI)
:returns: Dictionary with the driver configuration
"""
config = {}
config['services'] = {}
config['fs'] = {}
mandatory_parameters = ['username', 'password', 'mgmt_ip0']
optional_parameters = ['ssc_cmd', 'chap_enabled',
'ssh_port', 'cluster_admin_ip0',
'ssh_private_key']
# Trying to get the mandatory parameters from cinder.conf
for opt in mandatory_parameters:
config[opt] = config_opts.safe_get('hnas_%(opt)s' % {'opt': opt})
# If there is at least one of the mandatory parameters in
# cinder.conf, we assume that we should use the configuration
# from this file.
# Otherwise, we use the configuration from the deprecated XML file.
for param in mandatory_parameters:
if config[param] is not None:
break
else:
return None
# Getting the optional parameters from cinder.conf
for opt in optional_parameters:
config[opt] = config_opts.safe_get('hnas_%(opt)s' % {'opt': opt})
# It's possible to have up to 4 pools configured.
for i in range(0, 4):
idx = six.text_type(i)
svc_vol_type = (config_opts.safe_get(
'hnas_svc%(idx)s_volume_type' % {'idx': idx}))
svc_hdp = (config_opts.safe_get(
'hnas_svc%(idx)s_hdp' % {'idx': idx}))
# It's mandatory to have at least 1 pool configured (svc_0)
if (idx == '0' or svc_vol_type is not None or
svc_hdp is not None):
config['services'][svc_vol_type] = {}
config['fs'][svc_hdp] = svc_hdp
config['services'][svc_vol_type]['hdp'] = svc_hdp
config['services'][svc_vol_type]['volume_type'] = svc_vol_type
if dv_type == 'iscsi':
svc_ip = (config_opts.safe_get(
'hnas_svc%(idx)s_iscsi_ip' % {'idx': idx}))
config['services'][svc_vol_type]['iscsi_ip'] = svc_ip
config['services'][svc_vol_type]['label'] = (
'svc_%(idx)s' % {'idx': idx})
# Checking to ensure that the pools configurations are complete
_check_conf_params(config, svc_vol_type, dv_type, idx)
return config

View File

@ -0,0 +1,7 @@
---
upgrade:
- HNAS drivers will now read configuration from cinder.conf.
deprecations:
- The XML configuration file used by the HNAS drivers is now
deprecated and will no longer be used in the future. Please
use cinder.conf for all driver configuration.