cinder/cinder/volume/drivers/nec/volume_common.py

908 lines
38 KiB
Python

#
# Copyright (c) 2016 NEC Corporation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import errno
import os
import re
import traceback
from lxml import etree
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 context
from cinder import exception
from cinder.i18n import _
from cinder.volume import configuration
from cinder.volume.drivers.nec import cli
from cinder.volume.drivers.san import san
from cinder.volume import qos_specs
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
FLAGS = cfg.CONF
mstorage_opts = [
cfg.IPOpt('nec_ismcli_fip',
default=None,
help='FIP address of M-Series Storage iSMCLI.'),
cfg.StrOpt('nec_ismcli_user',
default='',
help='User name for M-Series Storage iSMCLI.'),
cfg.StrOpt('nec_ismcli_password',
secret=True,
default='',
help='Password for M-Series Storage iSMCLI.'),
cfg.StrOpt('nec_ismcli_privkey',
default='',
help='Filename of RSA private key for '
'M-Series Storage iSMCLI.'),
cfg.StrOpt('nec_ldset',
default='',
help='M-Series Storage LD Set name for Compute Node.'),
cfg.StrOpt('nec_ldname_format',
default='LX:%s',
help='M-Series Storage LD name format for volumes.'),
cfg.StrOpt('nec_backup_ldname_format',
default='LX:%s',
help='M-Series Storage LD name format for snapshots.'),
cfg.StrOpt('nec_diskarray_name',
default='',
help='Diskarray name of M-Series Storage.'),
cfg.StrOpt('nec_ismview_dir',
default='/tmp/nec/cinder',
help='Output path of iSMview file.'),
cfg.IntOpt('nec_ssh_pool_port_number',
default=22,
help='Port number of ssh pool.'),
cfg.IntOpt('nec_unpairthread_timeout',
default=3600,
help='Timeout value of Unpairthread.'),
cfg.IntOpt('nec_backend_max_ld_count',
default=1024,
help='Maximum number of managing sessions.'),
cfg.BoolOpt('nec_actual_free_capacity',
default=False,
help='Return actual free capacity.'),
cfg.BoolOpt('nec_ismview_alloptimize',
default=False,
help='Use legacy iSMCLI command with optimization.'),
cfg.ListOpt('nec_pools',
default=[],
help='M-Series Storage pool numbers list to be used.'),
cfg.ListOpt('nec_backup_pools',
default=[],
help='M-Series Storage backup pool number to be used.'),
cfg.BoolOpt('nec_queryconfig_view',
default=False,
help='Use legacy iSMCLI command.'),
cfg.IntOpt('nec_iscsi_portals_per_cont',
default=0,
deprecated_for_removal=True,
help='Max number of iSCSI portals per controller. '
'0 => unlimited. '
'This option is deprecated and may '
'be removed in the next release.'),
cfg.BoolOpt('nec_auto_accesscontrol',
default=True,
help='Configure access control automatically.'),
cfg.StrOpt('nec_cv_ldname_format',
default='LX:__ControlVolume_%xh',
help='M-Series Storage Control Volume name format.'),
]
FLAGS.register_opts(mstorage_opts, group=configuration.SHARED_CONF_GROUP)
def convert_to_name(uuid):
alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
num = int(uuid.replace(("-"), ""), 16)
convertname = ""
while num != 0:
convertname = alnum[num % len(alnum)] + convertname
num = num - num % len(alnum)
num = num // len(alnum)
return convertname
def convert_to_id(value62):
alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
length = len(value62)
weight = 0
value = 0
index = 0
for i in reversed(range(0, length)):
num = alnum.find(value62[i])
if index != 0:
value += int(weight * (num))
else:
value = num
index += 1
weight = 62 ** index
value = '%032x' % value
uuid = value[0:8]
uuid += '-'
uuid += value[8:12]
uuid += '-'
uuid += value[12:16]
uuid += '-'
uuid += value[16:20]
uuid += '-'
uuid += value[20:]
return uuid
class MStorageVolumeCommon(object):
"""M-Series Storage volume common class."""
def do_setup(self, context):
self._context = context
def check_for_setup_error(self):
fip = self._configuration.safe_get('nec_ismcli_fip')
user = self._configuration.safe_get('nec_ismcli_user')
pw = self._configuration.safe_get('nec_ismcli_password')
key = self._configuration.safe_get('nec_ismcli_privkey')
pools = self._configuration.safe_get('nec_pools')
if fip is None or fip == '':
raise exception.ParameterNotFound(param='nec_ismcli_fip')
if user is None or user == '':
raise exception.ParameterNotFound(param='nec_ismcli_user')
if (pw is None or pw == '') and (key is None or key == ''):
msg = _('nec_ismcli_password nor nec_ismcli_privkey')
raise exception.ParameterNotFound(param=msg)
if pools is None or len(pools) == 0:
raise exception.ParameterNotFound(param='nec_pools')
def _set_config(self, configuration, host, driver_name):
self._configuration = configuration
self._host = host
self._driver_name = driver_name
self._configuration.append_config_values(mstorage_opts)
self._configuration.append_config_values(san.san_opts)
self._config_group = self._configuration.config_group
self._properties = self._set_properties()
self._cli = self._properties['cli']
def _create_ismview_dir(self,
ismview_dir,
diskarray_name,
driver_name,
host):
"""Create ismview directory."""
filename = diskarray_name
if filename == '':
filename = driver_name + '_' + host
ismview_path = os.path.join(ismview_dir, filename)
LOG.debug('ismview_path=%s.', ismview_path)
try:
if os.path.exists(ismview_path):
os.remove(ismview_path)
except OSError as e:
with excutils.save_and_reraise_exception() as ctxt:
if e.errno == errno.ENOENT:
ctxt.reraise = False
try:
os.makedirs(ismview_dir)
except OSError as e:
with excutils.save_and_reraise_exception() as ctxt:
if e.errno == errno.EEXIST:
ctxt.reraise = False
return ismview_path
def get_conf_properties(self):
confobj = self._configuration
pool_pools = []
for pool in confobj.safe_get('nec_pools'):
if pool.endswith('h'):
pool_pools.append(int(pool[:-1], 16))
else:
pool_pools.append(int(pool, 10))
pool_backup_pools = []
for pool in confobj.safe_get('nec_backup_pools'):
if pool.endswith('h'):
pool_backup_pools.append(int(pool[:-1], 16))
else:
pool_backup_pools.append(int(pool, 10))
return {
'cli_fip': confobj.safe_get('nec_ismcli_fip'),
'cli_user': confobj.safe_get('nec_ismcli_user'),
'cli_password': confobj.safe_get('nec_ismcli_password'),
'cli_privkey': confobj.safe_get('nec_ismcli_privkey'),
'pool_pools': pool_pools,
'pool_backup_pools': pool_backup_pools,
'pool_actual_free_capacity':
confobj.safe_get('nec_actual_free_capacity'),
'ldset_name': confobj.safe_get('nec_ldset'),
'ld_name_format': confobj.safe_get('nec_ldname_format'),
'ld_backupname_format':
confobj.safe_get('nec_backup_ldname_format'),
'ld_backend_max_count':
confobj.safe_get('nec_backend_max_ld_count'),
'thread_timeout': confobj.safe_get('nec_unpairthread_timeout'),
'ismview_dir': confobj.safe_get('nec_ismview_dir'),
'ismview_alloptimize': confobj.safe_get('nec_ismview_alloptimize'),
'ssh_pool_port_number':
confobj.safe_get('nec_ssh_pool_port_number'),
'diskarray_name': confobj.safe_get('nec_diskarray_name'),
'queryconfig_view': confobj.safe_get('nec_queryconfig_view'),
'portal_number': confobj.safe_get('nec_iscsi_portals_per_cont'),
'auto_accesscontrol': confobj.safe_get('nec_auto_accesscontrol'),
'cv_name_format': confobj.safe_get('nec_cv_ldname_format')
}
def _set_properties(self):
conf_properties = self.get_conf_properties()
ismview_path = self._create_ismview_dir(
conf_properties['ismview_dir'],
conf_properties['diskarray_name'],
self._driver_name,
self._host)
vendor_name, _product_dict = self.get_oem_parameter()
backend_name = self._configuration.safe_get('volume_backend_name')
ssh_timeout = self._configuration.safe_get('ssh_conn_timeout')
reserved_per = self._configuration.safe_get('reserved_percentage')
conf_properties['ssh_conn_timeout'] = ssh_timeout
conf_properties['reserved_percentage'] = reserved_per
conf_properties['ismview_path'] = ismview_path
conf_properties['vendor_name'] = vendor_name
conf_properties['products'] = _product_dict
conf_properties['backend_name'] = backend_name
conf_properties['cli'] = cli.MStorageISMCLI(conf_properties)
return conf_properties
def get_oem_parameter(self):
product = os.path.join(os.path.dirname(__file__), 'product.xml')
try:
with open(product, 'r') as f:
xml = f.read()
root = etree.fromstring(xml)
vendor_name = root.findall('./VendorName')[0].text
product_dict = {}
product_map = root.findall('./ProductMap/Product')
for s in product_map:
product_dict[s.attrib['Name']] = int(s.text, 10)
return vendor_name, product_dict
except OSError as e:
with excutils.save_and_reraise_exception() as ctxt:
if e.errno == errno.ENOENT:
ctxt.reraise = False
raise exception.NotFound(_('%s not found.') % product)
@staticmethod
def get_ldname(volid, volformat):
alnum = ('0123456789'
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
ldname = ""
num = int(volid.replace(("-"), ""), 16)
while num != 0:
ldname = alnum[num % len(alnum)] + ldname
num = num - num % len(alnum)
num = num // len(alnum)
return volformat % ldname
def get_ldset(self, ldsets):
ldset = None
if self._properties['ldset_name'] == '':
nldset = len(ldsets)
if nldset == 0:
msg = _('Logical Disk Set could not be found.')
raise exception.NotFound(msg)
else:
ldset = None
else:
if self._properties['ldset_name'] not in ldsets:
msg = (_('Logical Disk Set `%s` could not be found.') %
self._properties['ldset_name'])
raise exception.NotFound(msg)
ldset = ldsets[self._properties['ldset_name']]
return ldset
def get_pool_capacity(self, pools, ldsets):
pools = [pool for (pn, pool) in pools.items()
if len(self._properties['pool_pools']) == 0 or
pn in self._properties['pool_pools']]
free_capacity_gb = 0
total_capacity_gb = 0
for pool in pools:
# Convert to GB.
tmp_total = int(pool['total'] // units.Gi)
tmp_free = int(pool['free'] // units.Gi)
if free_capacity_gb < tmp_free:
total_capacity_gb = tmp_total
free_capacity_gb = tmp_free
return {'total_capacity_gb': total_capacity_gb,
'free_capacity_gb': free_capacity_gb}
def set_backend_max_ld_count(self, xml, root):
section = root.findall('./CMD_REQUEST')[0]
version = section.get('version').replace('Version ', '')[0:3]
version = float(version)
if version < 9.1:
if 512 < self._properties['ld_backend_max_count']:
self._properties['ld_backend_max_count'] = 512
else:
if 1024 < self._properties['ld_backend_max_count']:
self._properties['ld_backend_max_count'] = 1024
def get_diskarray_max_ld_count(self, xml, root):
max_ld_count = 0
for section in root.findall(
'./'
'CMD_REQUEST/'
'CHAPTER[@name="Disk Array"]/'
'OBJECT[@name="Disk Array"]/'
'SECTION[@name="Disk Array Detail Information"]'):
unit = section.find('./UNIT[@name="Product ID"]')
if unit is None:
msg = (_('UNIT[@name="Product ID"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
else:
product_id = unit.text
if product_id in self._properties['products']:
max_ld_count = self._properties['products'][product_id]
else:
max_ld_count = 8192
LOG.debug('UNIT[@name="Product ID"] unknown id. '
'productId=%s', product_id)
LOG.debug('UNIT[@name="Product ID"] max_ld_count=%d.',
max_ld_count)
return max_ld_count
def get_pool_config(self, xml, root):
pools = {}
for xmlobj in root.findall('./'
'CMD_REQUEST/'
'CHAPTER[@name="Pool"]/'
'OBJECT[@name="Pool"]'):
section = xmlobj.find('./SECTION[@name="Pool Detail Information"]')
if section is None:
msg = (_('SECTION[@name="Pool Detail Information"] '
'not found. line=%(line)d out="%(out)s"') %
{'line': xmlobj.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
unit = section.find('./UNIT[@name="Pool No.(h)"]')
if unit is None:
msg = (_('UNIT[@name="Pool No.(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
pool_num = int(unit.text, 16)
unit = section.find('UNIT[@name="Pool Capacity"]')
if unit is None:
msg = (_('UNIT[@name="Pool Capacity"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
total = int(unit.text, 10)
unit = section.find('UNIT[@name="Free Pool Capacity"]')
if unit is None:
msg = (_('UNIT[@name="Free Pool Capacity"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
free = int(unit.text, 10)
if self._properties['pool_actual_free_capacity']:
unit = section.find('UNIT[@name="Used Pool Capacity"]')
if unit is None:
msg = (_('UNIT[@name="Used Pool Capacity"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
used = int(unit.text, 10)
for section in xmlobj.findall('./SECTION[@name='
'"Virtual Capacity Pool '
'Information"]'):
unit = section.find('UNIT[@name="Actual Capacity"]')
if unit is None:
msg = (_('UNIT[@name="Actual Capacity"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
total = int(unit.text, 10)
free = total - used
pool = {'pool_num': pool_num,
'total': total,
'free': free,
'ld_list': []}
pools[pool_num] = pool
return pools
def get_ld_config(self, xml, root, pools):
lds = {}
used_ldns = []
for section in root.findall('./'
'CMD_REQUEST/'
'CHAPTER[@name="Logical Disk"]/'
'OBJECT[@name="Logical Disk"]/'
'SECTION[@name="LD Detail Information"]'):
unit = section.find('./UNIT[@name="LDN(h)"]')
if unit is None:
msg = (_('UNIT[@name="LDN(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
ldn = int(unit.text, 16)
unit = section.find('./UNIT[@name="OS Type"]')
if unit is None:
msg = (_('UNIT[@name="OS Type"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
ostype = unit.text if unit.text is not None else ''
unit = section.find('./UNIT[@name="LD Name"]')
if unit is None:
msg = (_('UNIT[@name="LD Name"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
ldname = ostype + ':' + unit.text
unit = section.find('./UNIT[@name="Pool No.(h)"]')
if unit is None:
msg = (_('UNIT[@name="Pool No.(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
pool_num = int(unit.text, 16)
unit = section.find('./UNIT[@name="LD Capacity"]')
if unit is None:
msg = (_('UNIT[@name="LD Capacity"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
# byte capacity transform GB capacity.
ld_capacity = int(unit.text, 10) // units.Gi
unit = section.find('./UNIT[@name="RPL Attribute"]')
if unit is None:
msg = (_('UNIT[@name="RPL Attribute"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
rplatr = unit.text
unit = section.find('./UNIT[@name="Purpose"]')
if unit is None:
msg = (_('UNIT[@name="Purpose"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
purpose = unit.text
ld = {'ldname': ldname,
'ldn': ldn,
'pool_num': pool_num,
'ld_capacity': ld_capacity,
'RPL Attribute': rplatr,
'Purpose': purpose}
pools[pool_num]['ld_list'].append(ld)
lds[ldname] = ld
used_ldns.append(ldn)
return lds, used_ldns
def get_iscsi_ldset_config(self, xml, root):
ldsets = {}
for xmlobj in root.findall('./'
'CMD_REQUEST/'
'CHAPTER[@name="Access Control"]/'
'OBJECT[@name="LD Set(iSCSI)"]'):
ldsetlds = {}
portals = []
initiators = []
for unit in xmlobj.findall('./SECTION[@name="Portal"]/'
'UNIT[@name="Portal"]'):
if not unit.text.startswith('0.0.0.0:'):
portals.append(unit.text)
for unit in xmlobj.findall('./SECTION[@name="Initiator List"]/'
'UNIT[@name="Initiator List"]'):
initiators.append(unit.text)
section = xmlobj.find('./SECTION[@name="LD Set(iSCSI)'
' Information"]')
if section is None:
return ldsets
unit = section.find('./UNIT[@name="Platform"]')
if unit is None:
msg = (_('UNIT[@name="Platform"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
platform = unit.text
unit = section.find('./UNIT[@name="LD Set Name"]')
if unit is None:
msg = (_('UNIT[@name="LD Set Name"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
ldsetname = platform + ':' + unit.text
unit = section.find('./UNIT[@name="Target Mode"]')
if unit is None:
msg = (_('UNIT[@name="Target Mode"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
tmode = unit.text
if tmode == 'Normal':
unit = section.find('./UNIT[@name="Target Name"]')
if unit is None:
msg = (_('UNIT[@name="Target Name"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
iqn = unit.text
for section in xmlobj.findall('./SECTION[@name='
'"LUN/LD List"]'):
unit = section.find('./UNIT[@name="LDN(h)"]')
if unit is None:
msg = (_('UNIT[@name="LDN(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
ldn = int(unit.text, 16)
unit = section.find('./UNIT[@name="LUN(h)"]')
if unit is None:
msg = (_('UNIT[@name="LUN(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
lun = int(unit.text, 16)
ld = {'ldn': ldn,
'lun': lun,
'iqn': iqn}
ldsetlds[ldn] = ld
elif tmode == 'Multi-Target':
for section in xmlobj.findall('./SECTION[@name='
'"Target Information For '
'Multi-Target Mode"]'):
unit = section.find('./UNIT[@name="Target Name"]')
if unit is None:
msg = (_('UNIT[@name="Target Name"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
iqn = unit.text
unit = section.find('./UNIT[@name="LDN(h)"]')
if unit is None:
msg = (_('UNIT[@name="LDN(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
if unit.text.startswith('-'):
continue
ldn = int(unit.text, 16)
unit = section.find('./UNIT[@name="LUN(h)"]')
if unit is None:
msg = (_('UNIT[@name="LUN(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
if unit.text.startswith('-'):
continue
lun = int(unit.text, 16)
ld = {'ldn': ldn,
'lun': lun,
'iqn': iqn}
ldsetlds[ldn] = ld
else:
LOG.debug('`%(mode)s` Unknown Target Mode. '
'line=%(line)d out="%(out)s"',
{'mode': tmode, 'line': unit.sourceline, 'out': xml})
ldset = {'ldsetname': ldsetname,
'protocol': 'iSCSI',
'mode': tmode,
'portal_list': portals,
'lds': ldsetlds,
'initiator_list': initiators}
ldsets[ldsetname] = ldset
return ldsets
def get_fc_ldset_config(self, xml, root):
ldsets = {}
for xmlobj in root.findall('./'
'CMD_REQUEST/'
'CHAPTER[@name="Access Control"]/'
'OBJECT[@name="LD Set(FC)"]'):
ldsetlds = {}
section = xmlobj.find('./SECTION[@name="LD Set(FC)'
' Information"]')
if section is None:
return ldsets
unit = section.find('./UNIT[@name="Platform"]')
if unit is None:
msg = (_('UNIT[@name="Platform"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
platform = unit.text
unit = section.find('./UNIT[@name="LD Set Name"]')
if unit is None:
msg = (_('UNIT[@name="LD Set Name"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
ldsetname = platform + ':' + unit.text
wwpns = []
ports = []
for section in xmlobj.findall('./SECTION[@name="Path List"]'):
unit = section.find('./UNIT[@name="Path"]')
if unit is None:
msg = (_('UNIT[@name="Path"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
if unit.text.find('(') != -1:
ports.append(unit.text)
else:
wwpns.append(unit.text)
for section in xmlobj.findall('./SECTION[@name="LUN/LD List"]'):
unit = section.find('./UNIT[@name="LDN(h)"]')
if unit is None:
msg = (_('UNIT[@name="LDN(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
ldn = int(unit.text, 16)
unit = section.find('./UNIT[@name="LUN(h)"]')
if unit is None:
msg = (_('UNIT[@name="LUN(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
lun = int(unit.text, 16)
ld = {'ldn': ldn,
'lun': lun}
ldsetlds[ldn] = ld
ldset = {'ldsetname': ldsetname,
'lds': ldsetlds,
'protocol': 'FC',
'wwpn': wwpns,
'port': ports}
ldsets[ldsetname] = ldset
return ldsets
def get_hostport_config(self, xml, root):
hostports = {}
for section in root.findall('./'
'CMD_REQUEST/'
'CHAPTER[@name="Controller"]/'
'OBJECT[@name="Host Port"]/'
'SECTION[@name="Host Director'
'/Host Port Information"]'):
unit = section.find('./UNIT[@name="Port No.(h)"]')
if unit is None:
msg = (_('UNIT[@name="Port No.(h)"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
units = unit.text.split('-')
director = int(units[0], 16)
port = int(units[1], 16)
unit = section.find('./UNIT[@name="IP Address"]')
if unit is None:
unit = section.find('./UNIT[@name="WWPN"]')
if unit is None:
msg = (_('UNIT[@name="WWPN"] not found. '
'line=%(line)d out="%(out)s"') %
{'line': section.sourceline, 'out': xml})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
wwpn = unit.text
hostport = {
'director': director,
'port': port,
'wwpn': wwpn,
'protocol': 'FC',
}
else:
ip = unit.text
if ip == '0.0.0.0':
continue
hostport = {
'director': director,
'port': port,
'ip': ip,
'protocol': 'iSCSI',
}
if director not in hostports:
hostports[director] = []
hostports[director].append(hostport)
return hostports
def configs(self, xml):
root = etree.fromstring(xml)
pools = self.get_pool_config(xml, root)
lds, used_ldns = self.get_ld_config(xml, root, pools)
iscsi_ldsets = self.get_iscsi_ldset_config(xml, root)
fc_ldsets = self.get_fc_ldset_config(xml, root)
hostports = self.get_hostport_config(xml, root)
diskarray_max_ld_count = self.get_diskarray_max_ld_count(xml, root)
self.set_backend_max_ld_count(xml, root)
ldsets = {}
ldsets.update(iscsi_ldsets)
ldsets.update(fc_ldsets)
return pools, lds, ldsets, used_ldns, hostports, diskarray_max_ld_count
def get_xml(self):
ismview_path = self._properties['ismview_path']
if os.path.exists(ismview_path) and os.path.isfile(ismview_path):
with open(ismview_path, 'r') as f:
xml = f.read()
LOG.debug('loaded from %s.', ismview_path)
else:
xml = self._cli.view_all(ismview_path, False, False)
return xml
def parse_xml(self):
try:
xml = self.get_xml()
return self.configs(xml)
except Exception:
LOG.debug('parse_xml Unexpected error. exception=%s',
traceback.format_exc())
xml = self._cli.view_all(self._properties['ismview_path'], False)
return self.configs(xml)
def get_volume_type_qos_specs(self, volume_type_id):
specs = {}
ctxt = context.get_admin_context()
if volume_type_id is not None:
volume_type = volume_types.get_volume_type(ctxt, volume_type_id)
qos_specs_id = volume_type.get('qos_specs_id')
if qos_specs_id is not None:
specs = qos_specs.get_qos_specs(ctxt, qos_specs_id)['specs']
LOG.debug('get_volume_type_qos_specs '
'volume_type=%(volume_type)s, '
'qos_specs_id=%(qos_spec_id)s, '
'specs=%(specs)s',
{'volume_type': volume_type,
'qos_spec_id': qos_specs_id,
'specs': specs})
return specs
def get_qos_parameters(self, specs, reset):
qos_params = {}
if 'upperlimit' in specs and specs['upperlimit'] is not None:
if self.validates_number(specs['upperlimit']) is True:
upper_limit = int(specs['upperlimit'], 10)
if ((upper_limit != 0) and
((upper_limit < 10) or (upper_limit > 1000000))):
raise exception.InvalidConfigurationValue(
value=upper_limit, option='upperlimit')
qos_params['upperlimit'] = upper_limit
else:
raise exception.InvalidConfigurationValue(
value=specs['upperlimit'], option='upperlimit')
else:
# 0: Set to no limit.(default)
# On the QoS function in NEC Storage, 0 means there is no
# limit.
# None: Keep current value.
qos_params['upperlimit'] = 0 if reset else None
if 'lowerlimit' in specs and specs['lowerlimit'] is not None:
if self.validates_number(specs['lowerlimit']) is True:
lower_limit = int(specs['lowerlimit'], 10)
if (lower_limit != 0 and (lower_limit < 10 or
lower_limit > 1000000)):
raise exception.InvalidConfigurationValue(
value=lower_limit, option='lowerlimit')
qos_params['lowerlimit'] = lower_limit
else:
raise exception.InvalidConfigurationValue(
value=specs['lowerlimit'], option='lowerlimit')
else:
# 0: Set to no limit.(default)
# On the QoS function in NEC Storage, 0 means there is no
# limit.
# None: Keep current value.
qos_params['lowerlimit'] = 0 if reset else None
if 'upperreport' in specs:
if specs['upperreport'] not in ['on', 'off']:
LOG.debug('Illegal arguments. '
'upperreport is not on or off.'
'upperreport=%s', specs['upperreport'])
qos_params['upperreport'] = 'off' if reset else None
else:
qos_params['upperreport'] = specs['upperreport']
else:
# off: Set to no report.(default)
# None: Keep current value.
qos_params['upperreport'] = 'off' if reset else None
return qos_params
def check_accesscontrol(self, ldsets, ld):
"""Check Logical disk is in-use or not."""
set_accesscontrol = False
for ldset in ldsets.values():
if ld['ldn'] in ldset['lds']:
set_accesscontrol = True
break
return set_accesscontrol
def validates_number(self, value):
return re.match(r'^(?![-+]0+$)[-+]?([1-9][0-9]*)?[0-9](\.[0-9]+)?$',
'%s' % value) and True or False