Introduce Hitachi VSP iSCSI driver
This patch introduces Hitachi VSP iSCSI driver. Signed-off-by: "Kazumasa Nomura <kazumasa.nomura.rx@hitachi.com>" DocImpact Implements: blueprint hitachi-vsp-fc-driver Change-Id: I65d38f88201d55315eb3bfbffb2a72aa88a91a0d Depends-on: Ife99bf138c86a2f9be91298064513073271f1239
This commit is contained in:
parent
5c815388e2
commit
3f6a9739b3
@ -116,6 +116,8 @@ from cinder.volume.drivers.hitachi import vsp_fc as \
|
||||
cinder_volume_drivers_hitachi_vspfc
|
||||
from cinder.volume.drivers.hitachi import vsp_horcm as \
|
||||
cinder_volume_drivers_hitachi_vsphorcm
|
||||
from cinder.volume.drivers.hitachi import vsp_iscsi as \
|
||||
cinder_volume_drivers_hitachi_vspiscsi
|
||||
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 \
|
||||
@ -294,6 +296,7 @@ def list_opts():
|
||||
cinder_volume_drivers_hitachi_vspcommon.common_opts,
|
||||
cinder_volume_drivers_hitachi_vspfc.fc_opts,
|
||||
cinder_volume_drivers_hitachi_vsphorcm.horcm_opts,
|
||||
cinder_volume_drivers_hitachi_vspiscsi.iscsi_opts,
|
||||
cinder_volume_drivers_hpe_hpe3parcommon.hpe3par_opts,
|
||||
cinder_volume_drivers_hpe_hpelefthandiscsi.hpelefthand_opts,
|
||||
cinder_volume_drivers_hpe_hpexpopts.FC_VOLUME_OPTS,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -95,8 +95,8 @@ common_opts = [
|
||||
cfg.BoolOpt(
|
||||
'vsp_group_request',
|
||||
default=False,
|
||||
help='If True, the driver will create host groups on storage ports '
|
||||
'as needed.'),
|
||||
help='If True, the driver will create host groups or iSCSI targets on '
|
||||
'storage ports as needed.'),
|
||||
]
|
||||
|
||||
_REQUIRED_COMMON_OPTS = [
|
||||
@ -151,6 +151,7 @@ class VSPCommon(object):
|
||||
'ldev_range': [],
|
||||
'ports': [],
|
||||
'wwns': {},
|
||||
'portals': {},
|
||||
'output_first': True,
|
||||
}
|
||||
|
||||
@ -627,6 +628,20 @@ class VSPCommon(object):
|
||||
if not self.conf.safe_get(opt):
|
||||
msg = utils.output_log(MSG.INVALID_PARAMETER, param=opt)
|
||||
raise exception.VSPError(msg)
|
||||
if self.storage_info['protocol'] == 'iSCSI':
|
||||
self.check_param_iscsi()
|
||||
|
||||
def check_param_iscsi(self):
|
||||
"""Check iSCSI-related parameter values and consistency among them."""
|
||||
if self.conf.vsp_use_chap_auth:
|
||||
if not self.conf.vsp_auth_user:
|
||||
msg = utils.output_log(MSG.INVALID_PARAMETER,
|
||||
param='vsp_auth_user')
|
||||
raise exception.VSPError(msg)
|
||||
if not self.conf.vsp_auth_password:
|
||||
msg = utils.output_log(MSG.INVALID_PARAMETER,
|
||||
param='vsp_auth_password')
|
||||
raise exception.VSPError(msg)
|
||||
|
||||
def _range2list(self, param):
|
||||
"""Analyze a 'xxx-xxx' string and return a list of two integers."""
|
||||
@ -674,7 +689,7 @@ class VSPCommon(object):
|
||||
|
||||
def init_cinder_hosts(self, **kwargs):
|
||||
"""Initialize server-storage connection."""
|
||||
targets = kwargs.pop('targets', {'info': {}, 'list': []})
|
||||
targets = kwargs.pop('targets', {'info': {}, 'list': [], 'iqns': {}})
|
||||
connector = cinder_utils.brick_get_connector_properties(
|
||||
multipath=self.conf.use_multipath_for_image_xfer,
|
||||
enforce_multipath=self.conf.enforce_multipath_for_image_xfer)
|
||||
@ -719,8 +734,9 @@ class VSPCommon(object):
|
||||
raise exception.VSPError(msg)
|
||||
|
||||
def _create_target(self, targets, port, connector, hba_ids):
|
||||
"""Create a host group for the specified storage port."""
|
||||
target_name, gid = self.create_target_to_storage(port, connector)
|
||||
"""Create a host group or an iSCSI target on the storage port."""
|
||||
target_name, gid = self.create_target_to_storage(port, connector,
|
||||
hba_ids)
|
||||
utils.output_log(MSG.OBJECT_CREATED, object='a target',
|
||||
details='port: %(port)s, gid: %(gid)s, target_name: '
|
||||
'%(target)s' %
|
||||
@ -735,13 +751,13 @@ class VSPCommon(object):
|
||||
targets['list'].append((port, gid))
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_target_to_storage(self, port, connector):
|
||||
"""Create a host group on the specified port."""
|
||||
def create_target_to_storage(self, port, connector, hba_ids):
|
||||
"""Create a host group or an iSCSI target on the specified port."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_target_mode(self, port, gid):
|
||||
"""Configure the host group to meet the environment."""
|
||||
"""Configure the target to meet the environment."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
@ -751,7 +767,7 @@ class VSPCommon(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_target_from_storage(self, port, gid):
|
||||
"""Delete the host group from the port."""
|
||||
"""Delete the host group or the iSCSI target from the port."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def output_param_to_log(self):
|
||||
@ -777,6 +793,7 @@ class VSPCommon(object):
|
||||
'info': {},
|
||||
'list': [],
|
||||
'lun': {},
|
||||
'iqns': {},
|
||||
}
|
||||
ldev = utils.get_ldev(volume)
|
||||
# When 'ldev' is 0, it should be true.
|
||||
@ -813,10 +830,18 @@ class VSPCommon(object):
|
||||
multipath = connector.get('multipath', False)
|
||||
if self.storage_info['protocol'] == 'FC':
|
||||
data = self.get_properties_fc(targets)
|
||||
elif self.storage_info['protocol'] == 'iSCSI':
|
||||
data = self.get_properties_iscsi(targets, multipath)
|
||||
if target_lun is not None:
|
||||
data['target_discovered'] = False
|
||||
if not multipath or self.storage_info['protocol'] == 'FC':
|
||||
data['target_lun'] = target_lun
|
||||
else:
|
||||
target_luns = []
|
||||
for target in targets['list']:
|
||||
if targets['lun'][target[0]]:
|
||||
target_luns.append(target_lun)
|
||||
data['target_luns'] = target_luns
|
||||
return data
|
||||
|
||||
def get_properties_fc(self, targets):
|
||||
@ -827,6 +852,27 @@ class VSPCommon(object):
|
||||
if targets['lun'][target[0]]]
|
||||
return data
|
||||
|
||||
def get_properties_iscsi(self, targets, multipath):
|
||||
"""Return iSCSI-specific server-LDEV connection info."""
|
||||
data = {}
|
||||
primary_target = targets['list'][0]
|
||||
if not multipath:
|
||||
data['target_portal'] = self.storage_info[
|
||||
'portals'][primary_target[0]]
|
||||
data['target_iqn'] = targets['iqns'][primary_target]
|
||||
else:
|
||||
data['target_portals'] = [
|
||||
self.storage_info['portals'][target[0]] for target in
|
||||
targets['list'] if targets['lun'][target[0]]]
|
||||
data['target_iqns'] = [
|
||||
targets['iqns'][target] for target in targets['list']
|
||||
if targets['lun'][target[0]]]
|
||||
if self.conf.vsp_use_chap_auth:
|
||||
data['auth_method'] = 'CHAP'
|
||||
data['auth_username'] = self.conf.vsp_auth_user
|
||||
data['auth_password'] = self.conf.vsp_auth_password
|
||||
return data
|
||||
|
||||
@coordination.synchronized('vsp-host-{self.conf.vsp_storage_id}-'
|
||||
'{connector[host]}')
|
||||
def terminate_connection(self, volume, connector):
|
||||
@ -834,6 +880,7 @@ class VSPCommon(object):
|
||||
targets = {
|
||||
'info': {},
|
||||
'list': [],
|
||||
'iqns': {},
|
||||
}
|
||||
mapped_targets = {
|
||||
'list': [],
|
||||
@ -857,11 +904,12 @@ class VSPCommon(object):
|
||||
unmap_targets['list'].sort(reverse=True)
|
||||
self.unmap_ldev(unmap_targets, ldev)
|
||||
|
||||
target_wwn = [
|
||||
self.storage_info['wwns'][port_gid[:utils.PORT_ID_LENGTH]]
|
||||
for port_gid in unmap_targets['list']]
|
||||
return {'driver_volume_type': self.driver_info['volume_type'],
|
||||
'data': {'target_wwn': target_wwn}}
|
||||
if self.storage_info['protocol'] == 'FC':
|
||||
target_wwn = [
|
||||
self.storage_info['wwns'][port_gid[:utils.PORT_ID_LENGTH]]
|
||||
for port_gid in unmap_targets['list']]
|
||||
return {'driver_volume_type': self.driver_info['volume_type'],
|
||||
'data': {'target_wwn': target_wwn}}
|
||||
|
||||
@abc.abstractmethod
|
||||
def find_mapped_targets_from_storage(self, targets, ldev, target_ports):
|
||||
|
@ -238,7 +238,7 @@ def horcmgr_synchronized(func):
|
||||
|
||||
|
||||
def _is_valid_target(target, target_name, target_ports, is_pair):
|
||||
"""Return True if the specified host group is valid, False otherwise."""
|
||||
"""Return True if the specified target is valid, False otherwise."""
|
||||
if is_pair:
|
||||
return (target[:utils.PORT_ID_LENGTH] in target_ports and
|
||||
target_name == _PAIR_TARGET_NAME)
|
||||
@ -957,7 +957,7 @@ class VSPHORCM(common.VSPCommon):
|
||||
interval=interval, success_code=success_code, timeout=timeout)
|
||||
LOG.debug(
|
||||
'Deleted logical unit path of the specified logical '
|
||||
'device. (LDEV: %(ldev)s, host group: %(target)s)',
|
||||
'device. (LDEV: %(ldev)s, target: %(target)s)',
|
||||
{'ldev': ldev, 'target': target})
|
||||
|
||||
def find_all_mapped_targets_from_storage(self, targets, ldev):
|
||||
@ -968,7 +968,7 @@ class VSPHORCM(common.VSPCommon):
|
||||
targets['list'].append(port.split()[0])
|
||||
|
||||
def delete_target_from_storage(self, port, gid):
|
||||
"""Delete the host group from the port."""
|
||||
"""Delete the host group or the iSCSI target from the port."""
|
||||
result = self.run_raidcom(
|
||||
'delete', 'host_grp', '-port',
|
||||
'-'.join([port, gid]), do_raise=False)
|
||||
@ -1123,6 +1123,7 @@ HORCM_CMD
|
||||
targets = {
|
||||
'info': {},
|
||||
'list': [],
|
||||
'iqns': {},
|
||||
}
|
||||
super(VSPHORCM, self).init_cinder_hosts(targets=targets)
|
||||
self._init_pair_targets(targets['info'])
|
||||
@ -1141,7 +1142,7 @@ HORCM_CMD
|
||||
'wwpns': [_PAIR_TARGET_NAME_BODY],
|
||||
}
|
||||
target_name, gid = self.create_target_to_storage(
|
||||
port, connector)
|
||||
port, connector, None)
|
||||
utils.output_log(MSG.OBJECT_CREATED,
|
||||
object='a target for pair operation',
|
||||
details='port: %(port)s, gid: %(gid)s, '
|
||||
|
@ -66,7 +66,7 @@ class VSPHORCMFC(horcm.VSPHORCM):
|
||||
utils.output_log(MSG.SET_CONFIG_VALUE, object='port-wwn list',
|
||||
value=self.storage_info['wwns'])
|
||||
|
||||
def create_target_to_storage(self, port, connector):
|
||||
def create_target_to_storage(self, port, connector, hba_ids):
|
||||
"""Create a host group on the specified port."""
|
||||
wwpns = self.get_hba_ids_from_connector(connector)
|
||||
target_name = utils.TARGET_PREFIX + min(wwpns)
|
||||
|
184
cinder/volume/drivers/hitachi/vsp_horcm_iscsi.py
Normal file
184
cinder/volume/drivers/hitachi/vsp_horcm_iscsi.py
Normal file
@ -0,0 +1,184 @@
|
||||
# Copyright (C) 2016, Hitachi, Ltd.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
"""HORCM interface iSCSI module for Hitachi VSP Driver."""
|
||||
|
||||
import re
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from cinder import exception
|
||||
from cinder.volume.drivers.hitachi import vsp_horcm as horcm
|
||||
from cinder.volume.drivers.hitachi import vsp_utils as utils
|
||||
|
||||
_ISCSI_LINUX_MODE_OPTS = ['-host_mode', 'LINUX']
|
||||
_ISCSI_HOST_MODE_OPT = '-host_mode_opt'
|
||||
_ISCSI_HMO_REPORT_FULL_PORTAL = 83
|
||||
_ISCSI_TARGETS_PATTERN = re.compile(
|
||||
(r"^CL\w-\w+ +(?P<gid>\d+) +%s(?!pair00 )\S* +(?P<iqn>\S+) +"
|
||||
r"\w+ +\w +\d+ ") % utils.TARGET_PREFIX, re.M)
|
||||
_ISCSI_PORT_PATTERN = re.compile(
|
||||
r"^(CL\w-\w)\w* +ISCSI +TAR +\w+ +\w+ +\w +\w+ +Y ", re.M)
|
||||
_ISCSI_IPV4_ADDR_PATTERN = re.compile(
|
||||
r"^IPV4_ADDR +: +(?P<ipv4_addr>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$", re.M)
|
||||
_ISCSI_TCP_PORT_PATTERN = re.compile(
|
||||
r'^TCP_PORT\ +:\ +(?P<tcp_port>\d+)$', re.M)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
MSG = utils.VSPMsg
|
||||
|
||||
|
||||
class VSPHORCMISCSI(horcm.VSPHORCM):
|
||||
"""HORCM interface iscsi class for Hitachi VSP Driver.
|
||||
|
||||
Version history:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
1.0.0 - Initial driver.
|
||||
|
||||
"""
|
||||
|
||||
def connect_storage(self):
|
||||
"""Prepare for using the storage."""
|
||||
target_ports = self.conf.vsp_target_ports
|
||||
|
||||
super(VSPHORCMISCSI, self).connect_storage()
|
||||
result = self.run_raidcom('get', 'port')
|
||||
for port in _ISCSI_PORT_PATTERN.findall(result[1]):
|
||||
if (target_ports and port in target_ports and
|
||||
self._set_target_portal(port)):
|
||||
self.storage_info['ports'].append(port)
|
||||
|
||||
self.check_ports_info()
|
||||
utils.output_log(MSG.SET_CONFIG_VALUE,
|
||||
object='port-<IP address:port> list',
|
||||
value=self.storage_info['portals'])
|
||||
|
||||
def _set_target_portal(self, port):
|
||||
"""Get port info and store it in an instance variable."""
|
||||
ipv4_addr = None
|
||||
tcp_port = None
|
||||
result = self.run_raidcom(
|
||||
'get', 'port', '-port', port, '-key', 'opt')
|
||||
match = _ISCSI_IPV4_ADDR_PATTERN.search(result[1])
|
||||
if match:
|
||||
ipv4_addr = match.group('ipv4_addr')
|
||||
match = _ISCSI_TCP_PORT_PATTERN.search(result[1])
|
||||
if match:
|
||||
tcp_port = match.group('tcp_port')
|
||||
if not ipv4_addr or not tcp_port:
|
||||
return False
|
||||
self.storage_info['portals'][port] = ':'.join(
|
||||
[ipv4_addr, tcp_port])
|
||||
return True
|
||||
|
||||
def create_target_to_storage(self, port, connector, hba_ids):
|
||||
"""Create an iSCSI target on the specified port."""
|
||||
target_name = utils.TARGET_PREFIX + connector['ip']
|
||||
args = [
|
||||
'add', 'host_grp', '-port', port, '-host_grp_name', target_name]
|
||||
if hba_ids:
|
||||
args.extend(['-iscsi_name', hba_ids + utils.TARGET_IQN_SUFFIX])
|
||||
try:
|
||||
result = self.run_raidcom(*args)
|
||||
except exception.VSPError:
|
||||
result = self.run_raidcom('get', 'host_grp', '-port', port)
|
||||
hostgroup_pt = re.compile(
|
||||
r"^CL\w-\w+ +(?P<gid>\d+) +%s +\S+ " %
|
||||
target_name.replace('.', r'\.'), re.M)
|
||||
gid = hostgroup_pt.findall(result[1])
|
||||
if gid:
|
||||
return target_name, gid[0]
|
||||
else:
|
||||
raise
|
||||
return target_name, horcm.find_value(result[1], 'gid')
|
||||
|
||||
def set_hba_ids(self, port, gid, hba_ids):
|
||||
"""Connect the specified HBA with the specified port."""
|
||||
self.run_raidcom(
|
||||
'add', 'hba_iscsi', '-port', '-'.join([port, gid]),
|
||||
'-hba_iscsi_name', hba_ids)
|
||||
|
||||
def set_target_mode(self, port, gid):
|
||||
"""Configure the iSCSI target to meet the environment."""
|
||||
hostmode_setting = []
|
||||
hostmode_setting[:] = _ISCSI_LINUX_MODE_OPTS
|
||||
hostmode_setting.append(_ISCSI_HOST_MODE_OPT)
|
||||
hostmode_setting.append(_ISCSI_HMO_REPORT_FULL_PORTAL)
|
||||
self.run_raidcom(
|
||||
'modify', 'host_grp', '-port',
|
||||
'-'.join([port, gid]), *hostmode_setting)
|
||||
|
||||
def find_targets_from_storage(self, targets, connector, target_ports):
|
||||
"""Find mapped ports, memorize them and return unmapped port count."""
|
||||
nr_not_found = 0
|
||||
target_name = utils.TARGET_PREFIX + connector['ip']
|
||||
success_code = horcm.HORCM_EXIT_CODE.union([horcm.EX_ENOOBJ])
|
||||
iqn = self.get_hba_ids_from_connector(connector)
|
||||
iqn_pattern = re.compile(
|
||||
r'^CL\w-\w+ +\d+ +\S+ +%s ' % iqn, re.M)
|
||||
|
||||
for port in target_ports:
|
||||
targets['info'][port] = False
|
||||
|
||||
result = self.run_raidcom(
|
||||
'get', 'hba_iscsi', '-port', port, target_name,
|
||||
success_code=success_code)
|
||||
if iqn_pattern.search(result[1]):
|
||||
gid = result[1].splitlines()[1].split()[1]
|
||||
targets['info'][port] = True
|
||||
targets['list'].append((port, gid))
|
||||
continue
|
||||
|
||||
result = self.run_raidcom(
|
||||
'get', 'host_grp', '-port', port)
|
||||
for gid, iqn in _ISCSI_TARGETS_PATTERN.findall(result[1]):
|
||||
result = self.run_raidcom(
|
||||
'get', 'hba_iscsi', '-port', '-'.join([port, gid]))
|
||||
if iqn_pattern.search(result[1]):
|
||||
targets['info'][port] = True
|
||||
targets['list'].append((port, gid))
|
||||
targets['iqns'][(port, gid)] = iqn
|
||||
break
|
||||
else:
|
||||
nr_not_found += 1
|
||||
|
||||
return nr_not_found
|
||||
|
||||
def get_properties_iscsi(self, targets, multipath):
|
||||
"""Check if specified iSCSI targets exist and store their IQNs."""
|
||||
if not multipath:
|
||||
target_list = targets['list'][:1]
|
||||
else:
|
||||
target_list = targets['list'][:]
|
||||
|
||||
for target in target_list:
|
||||
if target not in targets['iqns']:
|
||||
port, gid = target
|
||||
result = self.run_raidcom('get', 'host_grp', '-port', port)
|
||||
match = re.search(
|
||||
r"^CL\w-\w+ +%s +\S+ +(?P<iqn>\S+) +\w+ +\w +\d+ " % gid,
|
||||
result[1], re.M)
|
||||
if not match:
|
||||
msg = utils.output_log(MSG.RESOURCE_NOT_FOUND,
|
||||
resource='Target IQN')
|
||||
raise exception.VSPError(msg)
|
||||
targets['iqns'][target] = match.group('iqn')
|
||||
LOG.debug('Found iqn of the iSCSI target. (port: %(port)s, '
|
||||
'gid: %(gid)s, target iqn: %(iqn)s)',
|
||||
{'port': port, 'gid': gid,
|
||||
'iqn': match.group('iqn')})
|
||||
return super(VSPHORCMISCSI, self).get_properties_iscsi(
|
||||
targets, multipath)
|
185
cinder/volume/drivers/hitachi/vsp_iscsi.py
Normal file
185
cinder/volume/drivers/hitachi/vsp_iscsi.py
Normal file
@ -0,0 +1,185 @@
|
||||
# Copyright (C) 2016, Hitachi, Ltd.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
"""iSCSI module for Hitachi VSP Driver."""
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from cinder import interface
|
||||
from cinder.volume import driver
|
||||
from cinder.volume.drivers.hitachi import vsp_common as common
|
||||
from cinder.volume.drivers.hitachi import vsp_utils as utils
|
||||
|
||||
iscsi_opts = [
|
||||
cfg.BoolOpt(
|
||||
'vsp_use_chap_auth',
|
||||
default=False,
|
||||
help='If True, CHAP authentication will be applied to communication '
|
||||
'between hosts and any of the iSCSI targets on the storage ports.'),
|
||||
cfg.StrOpt(
|
||||
'vsp_auth_user',
|
||||
help='Name of the user used for CHAP authentication performed in '
|
||||
'communication between hosts and iSCSI targets on the storage ports.'),
|
||||
cfg.StrOpt(
|
||||
'vsp_auth_password',
|
||||
secret=True,
|
||||
help='Password corresponding to vsp_auth_user.'),
|
||||
]
|
||||
|
||||
MSG = utils.VSPMsg
|
||||
|
||||
_DRIVER_INFO = {
|
||||
'proto': 'iSCSI',
|
||||
'hba_id': 'initiator',
|
||||
'hba_id_type': 'iSCSI initiator IQN',
|
||||
'msg_id': {
|
||||
'target': MSG.CREATE_ISCSI_TARGET_FAILED,
|
||||
},
|
||||
'volume_backend_name': utils.DRIVER_PREFIX + 'iSCSI',
|
||||
'volume_opts': iscsi_opts,
|
||||
'volume_type': 'iscsi',
|
||||
}
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(iscsi_opts)
|
||||
|
||||
|
||||
@interface.volumedriver
|
||||
class VSPISCSIDriver(driver.ISCSIDriver):
|
||||
"""iSCSI class for Hitachi VSP Driver.
|
||||
|
||||
Version history:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
1.0.0 - Initial driver.
|
||||
|
||||
"""
|
||||
|
||||
VERSION = common.VERSION
|
||||
|
||||
# ThirdPartySystems wiki page
|
||||
CI_WIKI_NAME = "Hitachi_VSP_CI"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize instance variables."""
|
||||
utils.output_log(MSG.DRIVER_INITIALIZATION_START,
|
||||
driver=self.__class__.__name__,
|
||||
version=self.get_version())
|
||||
super(VSPISCSIDriver, self).__init__(*args, **kwargs)
|
||||
|
||||
self.configuration.append_config_values(common.common_opts)
|
||||
self.configuration.append_config_values(iscsi_opts)
|
||||
self.common = utils.import_object(
|
||||
self.configuration, _DRIVER_INFO, kwargs.get('db'))
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Error are checked in do_setup() instead of this method."""
|
||||
pass
|
||||
|
||||
@utils.output_start_end_log
|
||||
def create_volume(self, volume):
|
||||
"""Create a volume and return its properties."""
|
||||
return self.common.create_volume(volume)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Create a volume from a snapshot and return its properties."""
|
||||
return self.common.create_volume_from_snapshot(volume, snapshot)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def create_cloned_volume(self, volume, src_vref):
|
||||
"""Create a clone of the specified volume and return its properties."""
|
||||
return self.common.create_cloned_volume(volume, src_vref)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def delete_volume(self, volume):
|
||||
"""Delete the specified volume."""
|
||||
self.common.delete_volume(volume)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def create_snapshot(self, snapshot):
|
||||
"""Create a snapshot from a volume and return its properties."""
|
||||
return self.common.create_snapshot(snapshot)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def delete_snapshot(self, snapshot):
|
||||
"""Delete the specified snapshot."""
|
||||
self.common.delete_snapshot(snapshot)
|
||||
|
||||
def get_volume_stats(self, refresh=False):
|
||||
"""Return properties, capabilities and current states of the driver."""
|
||||
return self.common.get_volume_stats(refresh)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def update_migrated_volume(
|
||||
self, ctxt, volume, new_volume, original_volume_status):
|
||||
"""Do any remaining jobs after migration."""
|
||||
self.common.discard_zero_page(new_volume)
|
||||
super(VSPISCSIDriver, self).update_migrated_volume(
|
||||
ctxt, volume, new_volume, original_volume_status)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def copy_image_to_volume(self, context, volume, image_service, image_id):
|
||||
"""Fetch the image from image_service and write it to the volume."""
|
||||
super(VSPISCSIDriver, self).copy_image_to_volume(
|
||||
context, volume, image_service, image_id)
|
||||
self.common.discard_zero_page(volume)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def extend_volume(self, volume, new_size):
|
||||
"""Extend the specified volume to the specified size."""
|
||||
self.common.extend_volume(volume, new_size)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
"""Return volume properties which Cinder needs to manage the volume."""
|
||||
return self.common.manage_existing(existing_ref)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def manage_existing_get_size(self, volume, existing_ref):
|
||||
"""Return the size[GB] of the specified volume."""
|
||||
return self.common.manage_existing_get_size(existing_ref)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def unmanage(self, volume):
|
||||
"""Prepare the volume for removing it from Cinder management."""
|
||||
self.common.unmanage(volume)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def do_setup(self, context):
|
||||
"""Prepare for the startup of the driver."""
|
||||
self.common.do_setup(context)
|
||||
|
||||
def ensure_export(self, context, volume):
|
||||
"""Synchronously recreate an export for a volume."""
|
||||
pass
|
||||
|
||||
def create_export(self, context, volume, connector):
|
||||
"""Export the volume."""
|
||||
pass
|
||||
|
||||
def remove_export(self, context, volume):
|
||||
"""Remove an export for a volume."""
|
||||
pass
|
||||
|
||||
@utils.output_start_end_log
|
||||
def initialize_connection(self, volume, connector):
|
||||
"""Initialize connection between the server and the volume."""
|
||||
return self.common.initialize_connection(volume, connector)
|
||||
|
||||
@utils.output_start_end_log
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
"""Terminate connection between the server and the volume."""
|
||||
self.common.terminate_connection(volume, connector)
|
@ -43,11 +43,13 @@ _DRIVER_DIR = 'cinder.volume.drivers.hitachi'
|
||||
_DRIVERS = {
|
||||
'HORCM': {
|
||||
'FC': 'vsp_horcm_fc.VSPHORCMFC',
|
||||
'iSCSI': 'vsp_horcm_iscsi.VSPHORCMISCSI',
|
||||
},
|
||||
}
|
||||
|
||||
DRIVER_PREFIX = 'VSP'
|
||||
TARGET_PREFIX = 'HBSD-'
|
||||
TARGET_IQN_SUFFIX = '.hbsd-target'
|
||||
GIGABYTE_PER_BLOCK_SIZE = units.Gi / 512
|
||||
|
||||
MAX_PROCESS_WAITTIME = 24 * 60 * 60
|
||||
@ -131,8 +133,8 @@ class VSPMsg(enum.Enum):
|
||||
DELETE_TARGET_FAILED = {
|
||||
'msg_id': 306,
|
||||
'loglevel': base_logging.WARNING,
|
||||
'msg': _LW('A host group could not be deleted. (port: %(port)s, '
|
||||
'gid: %(id)s)'),
|
||||
'msg': _LW('A host group or an iSCSI target could not be deleted. '
|
||||
'(port: %(port)s, gid: %(id)s)'),
|
||||
'suffix': WARNING_SUFFIX
|
||||
}
|
||||
CREATE_HOST_GROUP_FAILED = {
|
||||
@ -141,6 +143,12 @@ class VSPMsg(enum.Enum):
|
||||
'msg': _LW('A host group could not be added. (port: %(port)s)'),
|
||||
'suffix': WARNING_SUFFIX
|
||||
}
|
||||
CREATE_ISCSI_TARGET_FAILED = {
|
||||
'msg_id': 309,
|
||||
'loglevel': base_logging.WARNING,
|
||||
'msg': _LW('An iSCSI target could not be added. (port: %(port)s)'),
|
||||
'suffix': WARNING_SUFFIX
|
||||
}
|
||||
UNMAP_LDEV_FAILED = {
|
||||
'msg_id': 310,
|
||||
'loglevel': base_logging.WARNING,
|
||||
@ -408,7 +416,7 @@ class VSPMsg(enum.Enum):
|
||||
NO_CONNECTED_TARGET = {
|
||||
'msg_id': 649,
|
||||
'loglevel': base_logging.ERROR,
|
||||
'msg': _LE('The host group was not found.'),
|
||||
'msg': _LE('The host group or iSCSI target was not found.'),
|
||||
'suffix': ERROR_SUFFIX
|
||||
}
|
||||
RESOURCE_NOT_FOUND = {
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Adds new Hitachi VSP iSCSI Driver.
|
Loading…
x
Reference in New Issue
Block a user