diff --git a/ironic_python_agent/errors.py b/ironic_python_agent/errors.py
index 6b98fbd06..eb4c9c6eb 100644
--- a/ironic_python_agent/errors.py
+++ b/ironic_python_agent/errors.py
@@ -310,10 +310,18 @@ class ISCSIError(RESTError):
 
     message = 'Error starting iSCSI target'
 
+    def __init__(self, error_msg):
+        details = 'Error starting iSCSI target: {0}'.format(error_msg)
+        super(ISCSIError, self).__init__(details)
+
+
+class ISCSICommandError(ISCSIError):
+    """Error executing TGT command."""
+
     def __init__(self, error_msg, exit_code, stdout, stderr):
         details = ('{0}. Failed with exit code {1}. stdout: {2}. stderr: {3}')
         details = details.format(error_msg, exit_code, stdout, stderr)
-        super(ISCSIError, self).__init__(details)
+        super(ISCSICommandError, self).__init__(details)
 
 
 class DeviceNotFound(NotFound):
diff --git a/ironic_python_agent/extensions/iscsi.py b/ironic_python_agent/extensions/iscsi.py
index 57e50153f..8d38c6b24 100644
--- a/ironic_python_agent/extensions/iscsi.py
+++ b/ironic_python_agent/extensions/iscsi.py
@@ -19,6 +19,10 @@
 from oslo_concurrency import processutils
 from oslo_log import log
 from oslo_utils import uuidutils
+try:
+    import rtslib_fb
+except ImportError:
+    import rtslib as rtslib_fb
 
 from ironic_python_agent import errors
 from ironic_python_agent.extensions import base
@@ -33,10 +37,11 @@ def _execute(cmd, error_msg, **kwargs):
         stdout, stderr = utils.execute(*cmd, **kwargs)
     except processutils.ProcessExecutionError as e:
         LOG.error(error_msg)
-        raise errors.ISCSIError(error_msg, e.exit_code, e.stdout, e.stderr)
+        raise errors.ISCSICommandError(error_msg, e.exit_code,
+                                       e.stdout, e.stderr)
 
 
-def _wait_for_iscsi_daemon(attempts=10):
+def _wait_for_tgtd(attempts=10):
     """Wait for the ISCSI daemon to start."""
     # here, iscsi daemon is considered not running in case
     # tgtadm is not able to talk to tgtd to show iscsi targets
@@ -44,14 +49,12 @@ def _wait_for_iscsi_daemon(attempts=10):
     _execute(cmd, "ISCSI daemon didn't initialize", attempts=attempts)
 
 
-def _start_iscsi_daemon(iqn, device):
+def _start_tgtd(iqn, device):
     """Start a ISCSI target for the device."""
-    LOG.debug("Starting ISCSI target on device %(device)s", {'device': device})
-
     # Start ISCSI Target daemon
     _execute(['tgtd'], "Unable to start the ISCSI daemon")
 
-    _wait_for_iscsi_daemon()
+    _wait_for_tgtd()
 
     cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'target', '--op',
            'new', '--tid', '1', '--targetname', iqn]
@@ -67,15 +70,59 @@ def _start_iscsi_daemon(iqn, device):
                   "initiators for iqn %s" % iqn)
 
 
-class ISCSIExtension(base.BaseAgentExtension):
+def _start_lio(iqn, device):
+    try:
+        storage = rtslib_fb.BlockStorageObject(name=iqn, dev=device)
+        target = rtslib_fb.Target(rtslib_fb.FabricModule('iscsi'), iqn,
+                                  mode='create')
+        tpg = rtslib_fb.TPG(target, mode='create')
+        # disable all authentication
+        tpg.set_attribute('authentication', '0')
+        tpg.set_attribute('demo_mode_write_protect', '0')
+        tpg.set_attribute('generate_node_acls', '1')
+        # lun=1 is hardcoded in ironic
+        rtslib_fb.LUN(tpg, storage_object=storage, lun=1)
+        tpg.enable = 1
+    except rtslib_fb.utils.RTSLibError as exc:
+        msg = 'Failed to create a target: {0}'.format(exc)
+        raise errors.ISCSIError(msg)
 
+    try:
+        # bind to the default port on all interfaces
+        rtslib_fb.NetworkPortal(tpg, '0.0.0.0')
+    except rtslib_fb.utils.RTSLibError as exc:
+        msg = 'Failed to publish a target: {0}'.format(exc)
+        raise errors.ISCSIError(msg)
+
+
+class ISCSIExtension(base.BaseAgentExtension):
     @base.sync_command('start_iscsi_target')
     def start_iscsi_target(self, iqn=None):
         """Expose the disk as an ISCSI target."""
         # If iqn is not given, generate one
         if iqn is None:
-            iqn = 'iqn-' + uuidutils.generate_uuid()
+            iqn = 'iqn.2008-10.org.openstack:%s' % uuidutils.generate_uuid()
 
         device = hardware.dispatch_to_managers('get_os_install_device')
-        _start_iscsi_daemon(iqn, device)
+        LOG.debug("Starting ISCSI target with iqn %(iqn)s on device "
+                  "%(device)s", {'iqn': iqn, 'device': device})
+
+        try:
+            rts_root = rtslib_fb.RTSRoot()
+        except (EnvironmentError, rtslib_fb.RTSLibError) as exc:
+            LOG.warn('Linux-IO is not available, falling back to TGT. '
+                     'Error: %s.', exc)
+            rts_root = None
+
+        if rts_root is None:
+            _start_tgtd(iqn, device)
+        else:
+            _start_lio(iqn, device)
+            LOG.debug('Linux-IO configuration: %s', rts_root.dump())
+
+        LOG.info('Created iSCSI target with iqn %(iqn)s on device %(dev)s '
+                 'using %(method)s',
+                 {'iqn': iqn, 'dev': device,
+                  'method': 'tgtd' if rts_root is None else 'linux-io'})
+
         return {"iscsi_target_iqn": iqn}
diff --git a/ironic_python_agent/tests/unit/extensions/test_iscsi.py b/ironic_python_agent/tests/unit/extensions/test_iscsi.py
index efd7b51b1..eaae2c50b 100644
--- a/ironic_python_agent/tests/unit/extensions/test_iscsi.py
+++ b/ironic_python_agent/tests/unit/extensions/test_iscsi.py
@@ -16,7 +16,6 @@
 #    under the License.
 
 import mock
-import time
 
 from oslo_concurrency import processutils
 from oslotest import base as test_base
@@ -29,11 +28,12 @@ from ironic_python_agent import utils
 
 @mock.patch.object(hardware, 'dispatch_to_managers')
 @mock.patch.object(utils, 'execute')
-@mock.patch.object(time, 'sleep', lambda *_: None)
-class TestISCSIExtension(test_base.BaseTestCase):
+@mock.patch.object(iscsi.rtslib_fb, 'RTSRoot',
+                   mock.Mock(side_effect=iscsi.rtslib_fb.RTSLibError()))
+class TestISCSIExtensionTgt(test_base.BaseTestCase):
 
     def setUp(self):
-        super(TestISCSIExtension, self).setUp()
+        super(TestISCSIExtensionTgt, self).setUp()
         self.agent_extension = iscsi.ISCSIExtension()
         self.fake_dev = '/dev/fake'
         self.fake_iqn = 'iqn-fake'
@@ -78,11 +78,11 @@ class TestISCSIExtension(test_base.BaseTestCase):
         mock_execute.assert_has_calls(expected)
         mock_dispatch.assert_called_once_with('get_os_install_device')
 
-    @mock.patch.object(iscsi, '_wait_for_iscsi_daemon')
+    @mock.patch.object(iscsi, '_wait_for_tgtd')
     def test_start_iscsi_target_fail_command(self, mock_wait_iscsi,
                                              mock_execute, mock_dispatch):
         mock_dispatch.return_value = self.fake_dev
-        mock_execute.side_effect = [('', ''),
+        mock_execute.side_effect = [('', ''), ('', ''),
                                     processutils.ProcessExecutionError('blah')]
         self.assertRaises(errors.ISCSIError,
                           self.agent_extension.start_iscsi_target,
@@ -94,3 +94,64 @@ class TestISCSIExtension(test_base.BaseTestCase):
                               '--targetname', self.fake_iqn)]
         mock_execute.assert_has_calls(expected)
         mock_dispatch.assert_called_once_with('get_os_install_device')
+
+
+_ORIG_UTILS = iscsi.rtslib_fb.utils
+
+
+@mock.patch.object(hardware, 'dispatch_to_managers')
+# Don't mock the utils module, as it contains exceptions
+@mock.patch.object(iscsi, 'rtslib_fb', utils=_ORIG_UTILS)
+class TestISCSIExtensionLIO(test_base.BaseTestCase):
+
+    def setUp(self):
+        super(TestISCSIExtensionLIO, self).setUp()
+        self.agent_extension = iscsi.ISCSIExtension()
+        self.fake_dev = '/dev/fake'
+        self.fake_iqn = 'iqn-fake'
+
+    def test_start_iscsi_target(self, mock_rtslib, mock_dispatch):
+        mock_dispatch.return_value = self.fake_dev
+        result = self.agent_extension.start_iscsi_target(iqn=self.fake_iqn)
+
+        self.assertEqual({'iscsi_target_iqn': self.fake_iqn},
+                         result.command_result)
+        mock_rtslib.BlockStorageObject.assert_called_once_with(
+            name=self.fake_iqn, dev=self.fake_dev)
+        mock_rtslib.Target.assert_called_once_with(mock.ANY, self.fake_iqn,
+                                                   mode='create')
+        mock_rtslib.TPG.assert_called_once_with(
+            mock_rtslib.Target.return_value, mode='create')
+        mock_rtslib.LUN.assert_called_once_with(
+            mock_rtslib.TPG.return_value,
+            storage_object=mock_rtslib.BlockStorageObject.return_value,
+            lun=1)
+        mock_rtslib.NetworkPortal.assert_called_once_with(
+            mock_rtslib.TPG.return_value, '0.0.0.0')
+
+    def test_failed_to_start_iscsi(self, mock_rtslib, mock_dispatch):
+        mock_dispatch.return_value = self.fake_dev
+        mock_rtslib.Target.side_effect = _ORIG_UTILS.RTSLibError()
+        self.assertRaisesRegexp(
+            errors.ISCSIError, 'Failed to create a target',
+            self.agent_extension.start_iscsi_target, iqn=self.fake_iqn)
+
+    def test_failed_to_bind_iscsi(self, mock_rtslib, mock_dispatch):
+        mock_dispatch.return_value = self.fake_dev
+        mock_rtslib.NetworkPortal.side_effect = _ORIG_UTILS.RTSLibError()
+        self.assertRaisesRegexp(
+            errors.ISCSIError, 'Failed to publish a target',
+            self.agent_extension.start_iscsi_target, iqn=self.fake_iqn)
+
+        mock_rtslib.BlockStorageObject.assert_called_once_with(
+            name=self.fake_iqn, dev=self.fake_dev)
+        mock_rtslib.Target.assert_called_once_with(mock.ANY, self.fake_iqn,
+                                                   mode='create')
+        mock_rtslib.TPG.assert_called_once_with(
+            mock_rtslib.Target.return_value, mode='create')
+        mock_rtslib.LUN.assert_called_once_with(
+            mock_rtslib.TPG.return_value,
+            storage_object=mock_rtslib.BlockStorageObject.return_value,
+            lun=1)
+        mock_rtslib.NetworkPortal.assert_called_once_with(
+            mock_rtslib.TPG.return_value, '0.0.0.0')
diff --git a/requirements.txt b/requirements.txt
index 0d77faf44..c68a4b503 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,6 +18,7 @@ Pint>=0.5 # BSD
 psutil<2.0.0,>=1.1.1
 pyudev
 requests>=2.8.1
+rtslib-fb>=2.1.41
 six>=1.9.0
 stevedore>=1.5.0 # Apache-2.0
 WSME>=0.8