]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Implement IET target driver
authorMitsuhiro Tanino <mitsuhiro.tanino@hds.com>
Thu, 26 Feb 2015 00:16:39 +0000 (19:16 -0500)
committerMitsuhiro Tanino <mitsuhiro.tanino@hds.com>
Fri, 13 Mar 2015 18:15:20 +0000 (14:15 -0400)
When introducing Change-Id: I43190d1dac33748fe55fa00f260f32ab209be656,
IET driver was not implemented. This patch adds IET target for new
iscsi target driver model.

Certification results:
    https://bugs.launchpad.net/cinder/+bug/1428758

Closes-Bug: #1409918
Closes-Bug: #1329139

Co-Authored-By: Anish Bhatt <anish@chelsio.com>
Change-Id: I165a592bb3a39728fcc3d8ee4162b579c13ba928

cinder/tests/targets/test_iet_driver.py [new file with mode: 0644]
cinder/volume/targets/iet.py

diff --git a/cinder/tests/targets/test_iet_driver.py b/cinder/tests/targets/test_iet_driver.py
new file mode 100644 (file)
index 0000000..762cca4
--- /dev/null
@@ -0,0 +1,283 @@
+#    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 contextlib
+import os
+import shutil
+import StringIO
+import tempfile
+
+import mock
+from oslo_concurrency import processutils as putils
+from oslo_utils import timeutils
+
+from cinder import context
+from cinder import exception
+from cinder.openstack.common import fileutils
+from cinder import test
+from cinder import utils
+from cinder.volume import configuration as conf
+from cinder.volume.targets import iet
+
+
+class TestIetAdmDriver(test.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super(TestIetAdmDriver, self).__init__(*args, **kwargs)
+        self.configuration = conf.Configuration(None)
+        self.configuration.append_config_values = mock.Mock(return_value=0)
+        self.configuration.iscsi_ip_address = '10.9.8.7'
+        self.fake_project_id = 'ed2c1fd4-5fc0-11e4-aa15-123b93f75cba'
+        self.fake_volume_id = '83c2e877-feed-46be-8435-77884fe55b45'
+        self.target = iet.IetAdm(root_helper=utils.get_root_helper(),
+                                 configuration=self.configuration)
+        self.testvol =\
+            {'project_id': self.fake_project_id,
+             'name': 'testvol',
+             'size': 1,
+             'id': self.fake_volume_id,
+             'volume_type_id': None,
+             'provider_location': '10.9.8.7:3260 '
+                                  'iqn.2010-10.org.openstack:'
+                                  'volume-%s 0' % self.fake_volume_id,
+             'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
+                              'c76370d66b 2FE0CQ8J196R',
+             'provider_geometry': '512 512',
+             'created_at': timeutils.utcnow(),
+             'host': 'fake_host@lvm#lvm'}
+
+        self.expected_iscsi_properties = \
+            {'auth_method': 'CHAP',
+             'auth_password': '2FE0CQ8J196R',
+             'auth_username': 'stack-1-a60e2611875f40199931f2c76370d66b',
+             'encrypted': False,
+             'logical_block_size': '512',
+             'physical_block_size': '512',
+             'target_discovered': False,
+             'target_iqn': 'iqn.2010-10.org.openstack:volume-%s' %
+                           self.fake_volume_id,
+             'target_lun': 0,
+             'target_portal': '10.10.7.1:3260',
+             'volume_id': self.fake_volume_id}
+
+    def setUp(self):
+        super(TestIetAdmDriver, self).setUp()
+        self.fake_volumes_dir = tempfile.mkdtemp()
+        fileutils.ensure_tree(self.fake_volumes_dir)
+        self.addCleanup(self._cleanup)
+
+        self.exec_patcher = mock.patch.object(utils, 'execute')
+        self.mock_execute = self.exec_patcher.start()
+        self.addCleanup(self.exec_patcher.stop)
+
+    def _cleanup(self):
+        if os.path.exists(self.fake_volumes_dir):
+            shutil.rmtree(self.fake_volumes_dir)
+
+    def test_get_target(self):
+        tmp_file = StringIO.StringIO()
+        tmp_file.write(
+            'tid:1 name:iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45\n'  # noqa
+            '        sid:844427031282176 initiator:iqn.1994-05.com.redhat:5a6894679665\n'  # noqa
+            '               cid:0 ip:10.9.8.7 state:active hd:none dd:none')
+        tmp_file.seek(0)
+        with mock.patch('__builtin__.open') as mock_open:
+            mock_open.return_value = contextlib.closing(tmp_file)
+            self.assertEqual('1',
+                             self.target._get_target(
+                                                     'iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45'  # noqa
+                                                    ))
+
+            # Test the failure case: Failed to handle the config file
+            mock_open.side_effect = StandardError()
+            self.assertRaises(StandardError,
+                              self.target._get_target,
+                              '')
+
+    @mock.patch('os.path.exists', return_value=True)
+    @mock.patch('cinder.utils.temporary_chown')
+    def test_get_target_chap_auth(self, mock_chown, mock_exists):
+        tmp_file = StringIO.StringIO()
+        tmp_file.write(
+            'Target iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45\n'  # noqa
+            '    IncomingUser otzLy2UYbYfnP4zXLG5z 234Zweo38VGBBvrpK9nt\n'
+            '    Lun 0 Path=/dev/stack-volumes-lvmdriver-1/volume-83c2e877-feed-46be-8435-77884fe55b45,Type=fileio\n'  # noqa
+        )
+        tmp_file.seek(0)
+        test_vol = ('iqn.2010-10.org.openstack:'
+                    'volume-83c2e877-feed-46be-8435-77884fe55b45')
+        expected = ('otzLy2UYbYfnP4zXLG5z', '234Zweo38VGBBvrpK9nt')
+        with mock.patch('__builtin__.open') as mock_open:
+            ictx = context.get_admin_context()
+            mock_open.return_value = contextlib.closing(tmp_file)
+            self.assertEqual(expected,
+                             self.target._get_target_chap_auth(ictx, test_vol))
+            self.assertTrue(mock_open.called)
+
+            # Test the failure case: Failed to handle the config file
+            mock_open.side_effect = StandardError()
+            self.assertRaises(StandardError,
+                              self.target._get_target_chap_auth,
+                              ictx,
+                              test_vol)
+
+    @mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
+                return_value=0)
+    @mock.patch('cinder.utils.execute')
+    @mock.patch('os.path.exists', return_value=True)
+    @mock.patch('cinder.utils.temporary_chown')
+    def test_create_iscsi_target(self, mock_chown, mock_exists,
+                                 mock_execute, mock_get_targ):
+        mock_execute.return_value = ('', '')
+        tmp_file = StringIO.StringIO()
+        test_vol = ('iqn.2010-10.org.openstack:'
+                    'volume-83c2e877-feed-46be-8435-77884fe55b45')
+        with mock.patch('__builtin__.open') as mock_open:
+            mock_open.return_value = contextlib.closing(tmp_file)
+            self.assertEqual(
+                0,
+                self.target.create_iscsi_target(
+                    test_vol,
+                    0,
+                    0,
+                    self.fake_volumes_dir))
+            self.assertTrue(mock_execute.called)
+            self.assertTrue(mock_open.called)
+            self.assertTrue(mock_get_targ.called)
+
+            # Test the failure case: Failed to chown the config file
+            mock_open.side_effect = putils.ProcessExecutionError
+            self.assertRaises(exception.ISCSITargetCreateFailed,
+                              self.target.create_iscsi_target,
+                              test_vol,
+                              0,
+                              0,
+                              self.fake_volumes_dir)
+
+            # Test the failure case: Failed to set new auth
+            mock_execute.side_effect = putils.ProcessExecutionError
+            self.assertRaises(exception.ISCSITargetCreateFailed,
+                              self.target.create_iscsi_target,
+                              test_vol,
+                              0,
+                              0,
+                              self.fake_volumes_dir)
+
+    @mock.patch('cinder.utils.execute')
+    @mock.patch('os.path.exists', return_value=True)
+    def test_update_config_file_failure(self, mock_exists, mock_execute):
+        test_vol = ('iqn.2010-10.org.openstack:'
+                    'volume-83c2e877-feed-46be-8435-77884fe55b45')
+
+        # Test the failure case: conf file does not exist
+        mock_exists.return_value = False
+        mock_execute.side_effect = putils.ProcessExecutionError
+        self.assertRaises(exception.ISCSITargetCreateFailed,
+                          self.target.update_config_file,
+                          test_vol,
+                          0,
+                          self.fake_volumes_dir,
+                          "foo bar")
+
+    @mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
+                return_value=1)
+    @mock.patch('cinder.utils.execute')
+    def test_create_iscsi_target_already_exists(self, mock_execute,
+                                                mock_get_targ):
+        mock_execute.return_value = ('fake out', 'fake err')
+        test_vol = 'iqn.2010-10.org.openstack:'\
+                   'volume-83c2e877-feed-46be-8435-77884fe55b45'
+        self.assertEqual(
+            1,
+            self.target.create_iscsi_target(
+                test_vol,
+                1,
+                0,
+                self.fake_volumes_dir))
+        self.assertTrue(mock_get_targ.called)
+        self.assertTrue(mock_execute.called)
+
+    @mock.patch('cinder.volume.targets.iet.IetAdm._find_sid_cid_for_target',
+                return_value=None)
+    @mock.patch('os.path.exists', return_value=False)
+    @mock.patch.object(utils, 'execute')
+    def test_remove_iscsi_target(self, mock_execute, mock_exists, mock_find):
+
+        # Test the normal case
+        self.target.remove_iscsi_target(1,
+                                        0,
+                                        self.testvol['id'],
+                                        self.testvol['name'])
+        mock_execute.assert_any_calls('ietadm',
+                                      '--op',
+                                      'delete',
+                                      '--tid=1',
+                                      run_as_root=True)
+
+        # Test the failure case: putils.ProcessExecutionError
+        mock_execute.side_effect = putils.ProcessExecutionError
+        self.assertRaises(exception.ISCSITargetRemoveFailed,
+                          self.target.remove_iscsi_target,
+                          1,
+                          0,
+                          self.testvol['id'],
+                          self.testvol['name'])
+
+    def test_find_sid_cid_for_target(self):
+        tmp_file = StringIO.StringIO()
+        tmp_file.write(
+            'tid:1 name:iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45\n'  # noqa
+            '        sid:844427031282176 initiator:iqn.1994-05.com.redhat:5a6894679665\n'  # noqa
+            '               cid:0 ip:10.9.8.7 state:active hd:none dd:none')
+        tmp_file.seek(0)
+        with mock.patch('__builtin__.open') as mock_open:
+            mock_open.return_value = contextlib.closing(tmp_file)
+            self.assertEqual(('844427031282176', '0'),
+                             self.target._find_sid_cid_for_target(
+                                                     '1',
+                                                     'iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45',  # noqa
+                                                     'volume-83c2e877-feed-46be-8435-77884fe55b45'  # noqa
+                                                    ))
+
+    @mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
+                return_value=1)
+    @mock.patch('cinder.utils.execute')
+    @mock.patch.object(iet.IetAdm, '_get_target_chap_auth')
+    def test_create_export(self, mock_get_chap, mock_execute,
+                           mock_get_targ):
+        mock_execute.return_value = ('', '')
+        mock_get_chap.return_value = ('QZJbisGmn9AL954FNF4D',
+                                      'P68eE7u9eFqDGexd28DQ')
+        expected_result = {'location': '10.9.8.7:3260,1 '
+                           'iqn.2010-10.org.openstack:testvol 0',
+                           'auth': 'CHAP '
+                           'QZJbisGmn9AL954FNF4D P68eE7u9eFqDGexd28DQ'}
+        ctxt = context.get_admin_context()
+        self.assertEqual(expected_result,
+                         self.target.create_export(ctxt,
+                                                   self.testvol,
+                                                   self.fake_volumes_dir))
+        self.assertTrue(mock_execute.called)
+
+    @mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
+                return_value=1)
+    def test_ensure_export(self, mock_get_target):
+        ctxt = context.get_admin_context()
+        with mock.patch.object(self.target, 'create_iscsi_target'):
+            self.target.ensure_export(ctxt,
+                                      self.testvol,
+                                      self.fake_volumes_dir)
+            self.target.create_iscsi_target.assert_called_once_with(
+                'iqn.2010-10.org.openstack:testvol',
+                1, 0, self.fake_volumes_dir, None,
+                check_exit_code=False,
+                old_name=None)
index 3e3a2ff80cf44d009856ff3caedcbb93a5c9265f..9af7f0283f1278f8ba473d09841de13190ec3dc0 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import os
+import re
+import stat
 
-class IetAdm(object):
+from oslo_concurrency import processutils as putils
+from oslo_log import log as logging
+
+from cinder import exception
+from cinder.i18n import _LI, _LE, _LW
+from cinder import utils
+from cinder.volume.targets import iscsi
+
+LOG = logging.getLogger(__name__)
+
+
+class IetAdm(iscsi.ISCSITarget):
     VERSION = '0.1'
 
     def __init__(self, *args, **kwargs):
         super(IetAdm, self).__init__(*args, **kwargs)
+        self.iet_conf = self.configuration.safe_get('iet_conf')
+        self.iscsi_iotype = self.configuration.safe_get('iscsi_iotype')
+        self.auth_type = 'IncomingUser'
+        self.iet_sessions = '/proc/net/iet/session'
 
-    def _get_target_chap_auth(self, name):
-        pass
+    def _get_target(self, iqn):
 
-    def ensure_export(self, context, volume, volume_path):
-        pass
+        # Find existing iSCSI target session from /proc/net/iet/session
+        #
+        # tid:2 name:iqn.2010-10.org:volume-222
+        #     sid:562950561399296 initiator:iqn.1994-05.com:5a6894679665
+        #         cid:0 ip:192.168.122.1 state:active hd:none dd:none
+        # tid:1 name:iqn.2010-10.org:volume-111
+        #     sid:281475567911424 initiator:iqn.1994-05.com:5a6894679665
+        #         cid:0 ip:192.168.122.1 state:active hd:none dd:none
 
-    def create_export(self, context, volume, volume_path):
-        pass
+        iscsi_target = 0
+        try:
+            with open(self.iet_sessions, 'r') as f:
+                sessions = f.read()
+        except Exception:
+            LOG.exception(_LE("Failed to open iet session list for %s"), iqn)
+            raise
 
-    def remove_export(self, context, volume):
-        pass
+        session_list = re.split('^tid:(?m)', sessions)[1:]
+        for ses in session_list:
+            m = re.match('(\d+) name:(\S+)\s+', ses)
+            if m and iqn in m.group(2):
+                return m.group(1)
+
+        return iscsi_target
 
-    def initialize_connection(self, volume, connector):
+    def _get_iscsi_target(self, context, vol_id):
         pass
+
+    def _get_target_and_lun(self, context, volume):
+
+        # For ietadm dev starts at lun 0
+        lun = 0
+
+        # Using 0, ietadm tries to search empty tid for creating
+        # new iSCSI target
+        iscsi_target = 0
+
+        # Find existing iSCSI target based on iqn
+        iqn = '%svolume-%s' % (self.iscsi_target_prefix, volume['id'])
+        iscsi_target = self._get_target(iqn)
+
+        return iscsi_target, lun
+
+    def _get_target_chap_auth(self, context, name):
+
+        vol_id = name.split(':')[1]
+        if os.path.exists(self.iet_conf):
+            try:
+                with utils.temporary_chown(self.iet_conf):
+                    with open(self.iet_conf, 'r') as f:
+                        iet_conf_text = f.readlines()
+            except Exception:
+                # If we fail to handle config file, raise exception here to
+                # prevent unexpected behavior during subsequent operations.
+                LOG.exception(_LE("Failed to open config for %s."), vol_id)
+                raise
+
+            target_found = False
+            for line in iet_conf_text:
+                if target_found:
+                    m = re.search('(\w+) (\w+) (\w+)', line)
+                    if m:
+                        return (m.group(2), m.group(3))
+                    else:
+                        LOG.debug("Failed to find CHAP auth from config "
+                                  "for %s", vol_id)
+                        return None
+                elif name in line:
+                    target_found = True
+        else:
+            # Missing config file is unxepected sisuation. But we will create
+            # new config file during create_iscsi_target(). Just we warn the
+            # operator here.
+            LOG.warn(_LW("Failed to find CHAP auth from config for "
+                         "%(vol_id)s. Config file %(conf)s does not exist."),
+                     {'vol_id': vol_id, 'conf': self.iet_conf})
+            return None
+
+    def create_iscsi_target(self, name, tid, lun, path,
+                            chap_auth=None, **kwargs):
+
+        config_auth = None
+        vol_id = name.split(':')[1]
+
+        # Check the target is already existing.
+        tmp_tid = self._get_target(name)
+
+        # Create a new iSCSI target. If a target already exists,
+        # the command returns 234, but we ignore it.
+        try:
+            self._new_target(name, tid)
+            tid = self._get_target(name)
+            self._new_logicalunit(tid, lun, path)
+
+            if chap_auth is not None:
+                (username, password) = chap_auth
+                config_auth = ' '.join((self.auth_type,) + chap_auth)
+                self._new_auth(tid, self.auth_type, username, password)
+        except putils.ProcessExecutionError:
+            LOG.exception(_LE("Failed to create iscsi target for volume "
+                              "id:%s"), vol_id)
+            raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
+
+        # Update config file only if new scsi target is created.
+        if not tmp_tid:
+            self.update_config_file(name, tid, path, config_auth)
+
+        return tid
+
+    def update_config_file(self, name, tid, path, config_auth):
+
+        conf_file = self.iet_conf
+        vol_id = name.split(':')[1]
+
+        # If config file does not exist, create a blank conf file and
+        # add configuration for the volume on the new file.
+        if not os.path.exists(conf_file):
+            try:
+                utils.execute("truncate", conf_file, "--size=0",
+                              run_as_root=True)
+            except putils.ProcessExecutionError:
+                LOG.exception(_LE("Failed to create %(conf)s for volume "
+                                  "id:%(vol_id)s"),
+                              {'conf': conf_file, 'vol_id': vol_id})
+                raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
+
+        try:
+            volume_conf = """
+                    Target %s
+                        %s
+                        Lun 0 Path=%s,Type=%s
+            """ % (name, config_auth, path, self._iotype(path))
+
+            with utils.temporary_chown(conf_file):
+                with open(conf_file, 'a+') as f:
+                    f.write(volume_conf)
+        except Exception:
+            LOG.exception(_LE("Failed to update %(conf)s for volume "
+                              "id:%(vol_id)s"),
+                          {'conf': conf_file, 'vol_id': vol_id})
+            raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
+
+    def remove_iscsi_target(self, tid, lun, vol_id, vol_name, **kwargs):
+        LOG.info(_LI("Removing iscsi_target for volume: %s"), vol_id)
+
+        try:
+            self._delete_logicalunit(tid, lun)
+            session_info = self._find_sid_cid_for_target(tid, vol_name, vol_id)
+            if session_info:
+                sid, cid = session_info
+                self._force_delete_target(tid, sid, cid)
+
+            self._delete_target(tid)
+        except putils.ProcessExecutionError:
+            LOG.exception(_LE("Failed to remove iscsi target for volume "
+                              "id:%s"), vol_id)
+            raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
+
+        vol_uuid_file = vol_name
+        conf_file = self.iet_conf
+        if os.path.exists(conf_file):
+            try:
+                with utils.temporary_chown(conf_file):
+                    with open(conf_file, 'r+') as iet_conf_text:
+                        full_txt = iet_conf_text.readlines()
+                        new_iet_conf_txt = []
+                        count = 0
+                        for line in full_txt:
+                            if count > 0:
+                                count -= 1
+                                continue
+                            elif vol_uuid_file in line:
+                                count = 2
+                                continue
+                            else:
+                                new_iet_conf_txt.append(line)
+
+                        iet_conf_text.seek(0)
+                        iet_conf_text.truncate(0)
+                        iet_conf_text.writelines(new_iet_conf_txt)
+            except Exception:
+                LOG.exception(_LE("Failed to update %(conf)s for volume id "
+                                  "%(vol_id) after removing iscsi target"),
+                              {'conf': conf_file, 'vol_id': vol_id})
+                raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
+        else:
+            LOG.warn(_LW("Failed to update %(conf)s for volume id %(vol_id) "
+                         "after removing iscsi target. "
+                         "%(conf)s does not exist."),
+                     {'conf': conf_file, 'vol_id': vol_id})
+
+    def _find_sid_cid_for_target(self, tid, name, vol_id):
+        """Find sid, cid for existing iscsi target"""
+
+        try:
+            with open(self.iet_sessions, 'r') as f:
+                sessions = f.read()
+        except Exception as e:
+            LOG.info(_LI("Failed to open iet session list for "
+                         "%(vol_id)s: %(e)s"),
+                     {'vol_id': vol_id, 'e': e})
+            return None
+
+        session_list = re.split('^tid:(?m)', sessions)[1:]
+        for ses in session_list:
+            m = re.match('(\d+) name:(\S+)\s+sid:(\d+).+\s+cid:(\d+)', ses)
+            if m and tid in m.group(1) and name in m.group(2):
+                return m.group(3), m.group(4)
+
+    def _is_block(self, path):
+        mode = os.stat(path).st_mode
+        return stat.S_ISBLK(mode)
+
+    def _iotype(self, path):
+        if self.iscsi_iotype == 'auto':
+            return 'blockio' if self._is_block(path) else 'fileio'
+        else:
+            return self.iscsi_iotype
+
+    def _new_target(self, name, tid):
+        """Create new scsi target using specified parameters.
+
+        If the target already exists, ietadm returns
+        'Invalid argument' and error code '234'.
+        This should be ignored for ensure export case.
+        """
+        utils.execute('ietadm', '--op', 'new',
+                      '--tid=%s' % tid,
+                      '--params', 'Name=%s' % name,
+                      run_as_root=True, check_exit_code=[0, 234])
+
+    def _delete_target(self, tid):
+        utils.execute('ietadm', '--op', 'delete',
+                      '--tid=%s' % tid,
+                      run_as_root=True)
+
+    def _force_delete_target(self, tid, sid, cid):
+        utils.execute('ietadm', '--op', 'delete',
+                      '--tid=%s' % tid,
+                      '--sid=%s' % sid,
+                      '--cid=%s' % cid,
+                      run_as_root=True)
+
+    def show_target(self, tid, iqn=None):
+        utils.execute('ietadm', '--op', 'show',
+                      '--tid=%s' % tid,
+                      run_as_root=True)
+
+    def _new_logicalunit(self, tid, lun, path):
+        """Attach a new volume to scsi target as a logical unit.
+
+        If a logical unit exists on the specified target lun,
+        ietadm returns 'File exists' and error code '239'.
+        This should be ignored for ensure export case.
+        """
+
+        utils.execute('ietadm', '--op', 'new',
+                      '--tid=%s' % tid,
+                      '--lun=%d' % lun,
+                      '--params',
+                      'Path=%s,Type=%s' % (path, self._iotype(path)),
+                      run_as_root=True, check_exit_code=[0, 239])
+
+    def _delete_logicalunit(self, tid, lun):
+        utils.execute('ietadm', '--op', 'delete',
+                      '--tid=%s' % tid,
+                      '--lun=%d' % lun,
+                      run_as_root=True)
+
+    def _new_auth(self, tid, type, username, password):
+        utils.execute('ietadm', '--op', 'new',
+                      '--tid=%s' % tid,
+                      '--user',
+                      '--params=%s=%s,Password=%s' % (type,
+                                                      username,
+                                                      password),
+                      run_as_root=True)