1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
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.
|