220 lines
7.8 KiB
Python
220 lines
7.8 KiB
Python
# 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.
|
|
|
|
from oslo_concurrency import processutils as putils
|
|
from oslo_log import log as logging
|
|
|
|
from cinder import exception
|
|
from cinder import utils
|
|
from cinder.volume.targets import iscsi
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class LioAdm(iscsi.ISCSITarget):
|
|
"""iSCSI target administration for LIO using python-rtslib."""
|
|
def __init__(self, *args, **kwargs):
|
|
super(LioAdm, self).__init__(*args, **kwargs)
|
|
|
|
# FIXME(jdg): modify executor to use the cinder-rtstool
|
|
self.iscsi_target_prefix = self.configuration.safe_get('target_prefix')
|
|
|
|
self._verify_rtstool()
|
|
|
|
def _verify_rtstool(self):
|
|
try:
|
|
# This call doesn't need locking
|
|
utils.execute('cinder-rtstool', 'verify')
|
|
except (OSError, putils.ProcessExecutionError):
|
|
LOG.error('cinder-rtstool is not installed correctly')
|
|
raise
|
|
|
|
@staticmethod
|
|
@utils.synchronized('lioadm', external=True)
|
|
def _execute(*args, **kwargs):
|
|
"""Locked execution to prevent racing issues.
|
|
|
|
Racing issues are derived from a bug in RTSLib:
|
|
https://github.com/agrover/rtslib-fb/issues/36
|
|
"""
|
|
return utils.execute(*args, **kwargs)
|
|
|
|
def _get_target(self, iqn):
|
|
(out, err) = self._execute('cinder-rtstool',
|
|
'get-targets',
|
|
run_as_root=True)
|
|
lines = out.split('\n')
|
|
for line in lines:
|
|
if iqn in line:
|
|
return line
|
|
|
|
return None
|
|
|
|
def _get_targets(self):
|
|
(out, err) = self._execute('cinder-rtstool',
|
|
'get-targets',
|
|
run_as_root=True)
|
|
return out
|
|
|
|
def _get_iscsi_target(self, context, vol_id):
|
|
return 0
|
|
|
|
def _get_target_and_lun(self, context, volume):
|
|
lun = 0 # For lio, the lun starts at lun 0.
|
|
iscsi_target = 0 # NOTE: Not used by lio.
|
|
return iscsi_target, lun
|
|
|
|
def _persist_configuration(self, vol_id):
|
|
try:
|
|
self._execute('cinder-rtstool', 'save', run_as_root=True)
|
|
|
|
# On persistence failure we don't raise an exception, as target has
|
|
# been successfully created.
|
|
except putils.ProcessExecutionError:
|
|
LOG.warning("Failed to save iscsi LIO configuration when "
|
|
"modifying volume id: %(vol_id)s.",
|
|
{'vol_id': vol_id})
|
|
|
|
def _restore_configuration(self):
|
|
try:
|
|
self._execute('cinder-rtstool', 'restore', run_as_root=True)
|
|
|
|
# On persistence failure we don't raise an exception, as target has
|
|
# been successfully created.
|
|
except putils.ProcessExecutionError:
|
|
LOG.warning("Failed to restore iscsi LIO configuration.")
|
|
|
|
def create_iscsi_target(self, name, tid, lun, path,
|
|
chap_auth=None, **kwargs):
|
|
# tid and lun are not used
|
|
|
|
vol_id = name.split(':')[1]
|
|
|
|
LOG.info('Creating iscsi_target for volume: %s', vol_id)
|
|
|
|
chap_auth_userid = ""
|
|
chap_auth_password = ""
|
|
if chap_auth is not None:
|
|
(chap_auth_userid, chap_auth_password) = chap_auth
|
|
|
|
optional_args = []
|
|
if 'portals_port' in kwargs:
|
|
optional_args.append('-p%s' % kwargs['portals_port'])
|
|
|
|
if 'portals_ips' in kwargs:
|
|
optional_args.append('-a' + ','.join(kwargs['portals_ips']))
|
|
|
|
try:
|
|
command_args = ['cinder-rtstool',
|
|
'create',
|
|
path,
|
|
name,
|
|
chap_auth_userid,
|
|
chap_auth_password,
|
|
self.iscsi_protocol == 'iser'] + optional_args
|
|
self._execute(*command_args, run_as_root=True)
|
|
except putils.ProcessExecutionError:
|
|
LOG.exception("Failed to create iscsi target for volume "
|
|
"id:%s.", vol_id)
|
|
|
|
raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
|
|
|
|
iqn = '%s%s' % (self.iscsi_target_prefix, vol_id)
|
|
tid = self._get_target(iqn)
|
|
if tid is None:
|
|
LOG.error("Failed to create iscsi target for volume id:%s.",
|
|
vol_id)
|
|
raise exception.NotFound()
|
|
|
|
# We make changes persistent
|
|
self._persist_configuration(vol_id)
|
|
|
|
return tid
|
|
|
|
def remove_iscsi_target(self, tid, lun, vol_id, vol_name, **kwargs):
|
|
LOG.info('Removing iscsi_target: %s', vol_id)
|
|
vol_uuid_name = vol_name
|
|
iqn = '%s%s' % (self.iscsi_target_prefix, vol_uuid_name)
|
|
|
|
try:
|
|
self._execute('cinder-rtstool',
|
|
'delete',
|
|
iqn,
|
|
run_as_root=True)
|
|
except putils.ProcessExecutionError:
|
|
LOG.exception("Failed to remove iscsi target for volume id:%s.",
|
|
vol_id)
|
|
raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
|
|
|
|
# We make changes persistent
|
|
self._persist_configuration(vol_id)
|
|
|
|
def initialize_connection(self, volume, connector):
|
|
volume_iqn = volume['provider_location'].split(' ')[1]
|
|
|
|
(auth_method, auth_user, auth_pass) = \
|
|
volume['provider_auth'].split(' ', 3)
|
|
|
|
# Add initiator iqns to target ACL
|
|
try:
|
|
self._execute('cinder-rtstool', 'add-initiator',
|
|
volume_iqn,
|
|
auth_user,
|
|
auth_pass,
|
|
connector['initiator'],
|
|
run_as_root=True)
|
|
except putils.ProcessExecutionError:
|
|
LOG.exception("Failed to add initiator iqn %s to target",
|
|
connector['initiator'])
|
|
raise exception.ISCSITargetAttachFailed(
|
|
volume_id=volume['id'])
|
|
|
|
# We make changes persistent
|
|
self._persist_configuration(volume['id'])
|
|
|
|
return super(LioAdm, self).initialize_connection(volume, connector)
|
|
|
|
def terminate_connection(self, volume, connector, **kwargs):
|
|
if volume['provider_location'] is None:
|
|
LOG.debug('No provider_location for volume %s.',
|
|
volume['id'])
|
|
return
|
|
|
|
volume_iqn = volume['provider_location'].split(' ')[1]
|
|
|
|
# Delete initiator iqns from target ACL
|
|
try:
|
|
self._execute('cinder-rtstool', 'delete-initiator',
|
|
volume_iqn,
|
|
connector['initiator'],
|
|
run_as_root=True)
|
|
except putils.ProcessExecutionError:
|
|
LOG.exception(
|
|
"Failed to delete initiator iqn %s from target.",
|
|
connector['initiator'])
|
|
raise exception.ISCSITargetDetachFailed(volume_id=volume['id'])
|
|
|
|
# We make changes persistent
|
|
self._persist_configuration(volume['id'])
|
|
|
|
def ensure_export(self, context, volume, volume_path):
|
|
"""Recreate exports for logical volumes."""
|
|
|
|
# Restore saved configuration file if no target exists.
|
|
if not self._get_targets():
|
|
LOG.info('Restoring iSCSI target from configuration file')
|
|
self._restore_configuration()
|
|
return
|
|
|
|
LOG.info("Skipping ensure_export. Found existing iSCSI target.")
|