Add a walkthrough that demonstrates how to get started using rtslib. Signed-off-by: Andy Grover <agrover@redhat.com>
176 lines
6.0 KiB
Markdown
176 lines
6.0 KiB
Markdown
Getting started using the rtslib API
|
|
====================================
|
|
|
|
The rtslib API wraps the LIO kernel target's configfs-based userspace
|
|
configuration with an object-based, Python interface. Its operating
|
|
model is that instantiating a Python object will result in that object
|
|
creating the corresponding kernel object if it doesn't already exist,
|
|
or will refer to the existing object if it does.
|
|
|
|
Also, note that the Python objects wrap LIO's configfs objects, but do
|
|
no buffering or caching of values or properties. Setting a value on an
|
|
object via rtslib results in the LIO kernel configuration being
|
|
modified immediately as well -- there is no additional save or flush
|
|
required.
|
|
|
|
Let's try it.
|
|
|
|
> sudo python
|
|
|
|
Configuring LIO must be done by root, so run the python REPL as root.
|
|
|
|
>>> from rtslib import FileIOStorageObject
|
|
>>> f = FileIOStorageObject("test1", "/tmp/test.img", 100000000)
|
|
|
|
FileIO storage objects enable a file to serve as a disk image. In this
|
|
example, a few things are happening. First, since a backing file path
|
|
is given and does not yet exist, rtslib creates the backing file at
|
|
/tmp/test.img with a size of 100000000 bytes. Next, rtslib configures
|
|
LIO to use the file to back a fileio storage object called "test1".
|
|
|
|
The storage object has a number of properties.
|
|
|
|
>>> f.status
|
|
'deactivated'
|
|
|
|
This shows that while the backstore has been registered, it hasn't yet
|
|
been exported via a fabric. Now, let's create a fabric.
|
|
|
|
>>> from rtslib import FabricModule, Target, TPG
|
|
>>> iscsi = FabricModule("iscsi")
|
|
|
|
Fabric objects are singleton objects. The preferred way to obtain a
|
|
reference to one is via the FabricModule factory method, as
|
|
shown. Then, we can create a Target, an instance of that fabric.
|
|
|
|
>>> target = Target(iscsi)
|
|
|
|
For other fabrics that are linked to actual hardware resources, we
|
|
would have also needed to supply a valid "wwn" parameter that matched
|
|
available hardware IDs. But for iscsi, we can omit this and rtslib
|
|
will autogenerate one.
|
|
|
|
>>> target.wwn
|
|
'iqn.2003-01.org.linux-iscsi.localhost.x8664:sn.c11be18bebc3'
|
|
|
|
Next, we must create a TPG. iSCSI allows a single named target to have
|
|
multiple independent configurations within it, divided into Target
|
|
Port Groups, or TPGs. Usually one is enough, so we just need to create
|
|
one, and then all further configuration will be on the TPG.
|
|
|
|
>>> tpg = TPG(target, 1)
|
|
|
|
Our TPG needs to listen on a TCP port for incoming connections from
|
|
initiators, so let's set that up.
|
|
|
|
>>> from rtslib import NetworkPortal, NodeACL, LUN, MappedLUN
|
|
>>> portal = NetworkPortal(tpg, "0.0.0.0", 3260)
|
|
|
|
Now LIO is listening on all IP addresses, on port 3260, the iSCSI
|
|
default. But, we aren't exporting any luns yet!
|
|
|
|
>>> lun = LUN(tpg, 0, f)
|
|
|
|
We've just assigned the FileIO storage object to the TPG. as we can
|
|
see:
|
|
|
|
>>> f.status
|
|
'activated'
|
|
|
|
...the storage object is now active and linked to the TPG.
|
|
|
|
The final thing to configure is Node ACLs. There are some
|
|
authentication modes where just assigning a LUN to a TPG will export
|
|
it to all initiators (see targetcli manpage for more info), but
|
|
usually one creates individual permissions and LUN mappings for each
|
|
initiator.
|
|
|
|
LIO configures initiator access via initiator IQN, instead of some
|
|
other targets that are based on initiator IP address. (For open-iscsi,
|
|
the autogenerated initiator IQN is in
|
|
"/etc/iscsi/initiatorname.iscsi".)
|
|
|
|
>>> nodeacl = NodeACL(tpg, "iqn.2004-03.com.example.foo:0987")
|
|
|
|
When we are using nodeacl-based authentication, i.e. when
|
|
generate_node_acls is 0, we then need to map the tpg lun to the
|
|
nodeacl. This is handy in that each initiator can have its own view of
|
|
the available LUNs.
|
|
|
|
>>> mlun = MappedLUN(nodeacl, 5, lun)
|
|
|
|
Finding associated objects
|
|
--------------------------
|
|
|
|
All rtslib objects have properties to obtain other related objects. For
|
|
example, if you have a TPG object called "tpg", then tpg.parent_target
|
|
will be the Target that contains the tpg.
|
|
|
|
>>> tpg
|
|
<TPG 1>
|
|
|
|
>>> tpg.parent_target
|
|
<Target iqn.2003-01.org.linux-iscsi.localhost.x8664:sn.c11be18bebc3>
|
|
|
|
The FabricModule object is then accessible from the target:
|
|
|
|
>>> tpg.parent_target.fabric_module.name
|
|
'iscsi'
|
|
|
|
Going down the hierarchy, an object can have multiple child objects of
|
|
a given type, so a list is returned:
|
|
|
|
>>> iscsi.targets
|
|
<generator object _list_targets at 0x2836f00>
|
|
|
|
Actually it's a Python generator, list's less memory-intensive
|
|
cousin. If we really want a list, call:
|
|
|
|
>>> list(iscsi.targets)
|
|
[<Target iqn.2003-01.org.linux-iscsi.localhost.x8664:sn.c11be18bebc3>]
|
|
|
|
Finding objects using RTSRoot()
|
|
-------------------------------
|
|
|
|
The RTSRoot object enables finding all rtslib objects of a type that
|
|
are configured on the system.
|
|
|
|
>>> root = rtslib.RTSRoot()
|
|
>>> list(root.storage_objects)
|
|
[<FileIOStorageObject fileio/test1>]
|
|
|
|
RTSRoot contains generators for all levels of objects. For example, if
|
|
we want to obtain all NodeACL objects, instead of needing nested for
|
|
loops to iterate through all fabrics, then all targets, then all tpgs,
|
|
then all node acls, the root.node_acls generator iterates through
|
|
NodeACLs wherever they are:
|
|
|
|
>>> list(root.node_acls)
|
|
[<NodeACL iqn.2004-03.com.example.foo:0987>,
|
|
<NodeACL iqn.2004-03.com.example.foo:1234>]
|
|
|
|
Other handy things to try
|
|
-------------------------
|
|
|
|
All objects have a dump() method, which outputs a dict of the
|
|
object's current state.
|
|
|
|
>>> mlun.dump()
|
|
{'index': 5, 'tpg_lun': 0, 'write_protect': False}
|
|
|
|
Finally, rtslib is just a wrapper around LIO's configfs interface,
|
|
which is usually mounted at /sys/kernel/config/target. Poking around
|
|
there may also help to understand what's going on.
|
|
|
|
Other sample code
|
|
-----------------
|
|
|
|
While targetcli uses rtslib, it has a parallel configshell-based tree
|
|
structure that may make it less helpful as a reference. Focus on
|
|
'rtsnode' objects -- these are references to rtslib objects, as
|
|
described here.
|
|
|
|
Also, the 'targetd' project (https://github.com/agrover/targetd) uses
|
|
rtslib for its kernel target support, see 'targetd/block.py' for more
|
|
examples of rtslib usage.
|