* New method:
access_group_map_create(pool_name, vol_name, ag_name, h_lun_id=None):
Grant certain access group the rw access to defined volume.
Parameters:
pool_name (str): The name of pool which defined volume belongs to.
vol_name (str): The name of volume.
ag_name (str): Access group name
h_lun_id (int, optional):
Host LUN ID (the SCSI LUN ID seen by iSCSI initiator).
Range is 0 to 255.
If not defined, targetd will try to find the next available
one.
Returns:
N/A
Errors:
-1000: No free host_lun_id. LUN ID between 0 ~ 255 is in use.
-200: Access group not found.
Signed-off-by: Gris Ge <fge(a)redhat.com>
---
API.md | 16 +++++++++
targetd/block.py | 105 +++++++++++++++++++++++++++++++++++++++++--------------
targetd/utils.py | 2 ++
3 files changed, 97 insertions(+), 26 deletions(-)
diff --git a/API.md b/API.md
index 2c9b064..6fe769c 100644
--- a/API.md
+++ b/API.md
@@ -220,6 +220,22 @@ Returns:
Errors:
N/A
+### access_group_map_create(pool_name, vol_name, ag_name, h_lun_id=None)
+Grant certain access group the rw access to defined volume.
+Parameters:
+ pool_name (str): The name of pool which defined volume belongs to.
+ vol_name (str): The name of volume.
+ ag_name (str): Access group name
+ h_lun_id (int, optional):
+ Host LUN ID (the SCSI LUN ID seen by iSCSI initiator).
+ Range is 0 to 255.
+ If not defined, targetd will try to find the next available one.
+Returns:
+ N/A
+Errors:
+ -1000: No free host_lun_id. LUN ID between 0 ~ 255 is in use.
+ -200: Access group not found.
+
File system operations
----------------------
Ability to create different file systems and perform operation on them. The
diff --git a/targetd/block.py b/targetd/block.py
index 162c84e..3695f65 100644
--- a/targetd/block.py
+++ b/targetd/block.py
@@ -113,6 +113,7 @@ def initialize(config_dict):
access_group_init_add=access_group_init_add,
access_group_init_del=access_group_init_del,
access_group_map_list=access_group_map_list,
+ access_group_map_create=access_group_map_create,
)
@@ -201,24 +202,6 @@ def export_list(req):
def export_create(req, pool, vol, initiator_wwn, lun):
- # get wwn of volume so LIO can export as vpd83 info
- vg_name, thin_pool = get_vg_lv(pool)
-
- with vgopen(vg_name) as vg:
- vol_serial = vg.lvFromName(vol).getUuid()
-
- # only add new SO if it doesn't exist
- # so.name concats pool & vol names separated by ':'
- so_name = "%s:%s" % (vg_name, vol)
- try:
- so = BlockStorageObject(so_name)
- except RTSLibError:
- so = BlockStorageObject(so_name, dev="/dev/%s/%s" % (vg_name, vol))
- so.wwn = vol_serial
-
- # export useful scsi model if kernel > 3.8
- with ignored(RTSLibError):
- so.set_attribute("emulate_model_alias", '1')
fm = FabricModule('iscsi')
t = Target(fm, target_name)
@@ -228,14 +211,7 @@ def export_create(req, pool, vol, initiator_wwn, lun):
NetworkPortal(tpg, "0.0.0.0")
na = NodeACL(tpg, initiator_wwn)
- # only add tpg lun if it doesn't exist
- for tmp_lun in tpg.luns:
- if tmp_lun.storage_object.name == so.name \
- and tmp_lun.storage_object.plugin == 'block':
- tpg_lun = tmp_lun
- break
- else:
- tpg_lun = LUN(tpg, storage_object=so)
+ tpg_lun = _tpg_lun_of(tpg, pool, vol)
# only add mapped lun if it doesn't exist
for tmp_mlun in tpg_lun.mapped_luns:
@@ -518,3 +494,80 @@ def access_group_map_list(req):
)
return results
+
+
+def _tpg_lun_of(tpg, pool_name, vol_name):
+ """
+ Return a object of LUN for given lvm lv.
+ If not exist, create one.
+ """
+ # get wwn of volume so LIO can export as vpd83 info
+ vg_name, thin_pool = get_vg_lv(pool_name)
+
+ with vgopen(vg_name) as vg:
+ vol_serial = vg.lvFromName(vol_name).getUuid()
+
+ # only add new SO if it doesn't exist
+ # so.name concats pool & vol names separated by ':'
+ so_name = "%s:%s" % (vg_name, vol_name)
+ try:
+ so = BlockStorageObject(so_name)
+ except RTSLibError:
+ so = BlockStorageObject(
+ so_name, dev="/dev/%s/%s" % (vg_name, vol_name))
+ so.wwn = vol_serial
+
+ # export useful scsi model if kernel > 3.8
+ with ignored(RTSLibError):
+ so.set_attribute("emulate_model_alias", '1')
+
+ # only add tpg lun if it doesn't exist
+ for tmp_lun in tpg.luns:
+ if tmp_lun.storage_object.name == so.name and \
+ tmp_lun.storage_object.plugin == 'block':
+ return tmp_lun
+ else:
+ return LUN(tpg, storage_object=so)
+
+
+def access_group_map_create(req, pool_name, vol_name, ag_name, h_lun_id=None):
+ tpg = _get_iscsi_tpg()
+ tpg.enable = True
+ tpg.set_attribute("authentication", '0')
+ NetworkPortal(tpg, "0.0.0.0")
+
+ tpg_lun = _tpg_lun_of(tpg, pool_name, vol_name)
+
+ # Pre-Check:
+ # 1. Already mapped to requested access group, return None
+ if len(list(tpg_lun.mapped_luns)):
+ tgt_map_list = access_group_map_list(req)
+ for tgt_map in tgt_map_list:
+ if tgt_map['ag_name'] == ag_name and \
+ tgt_map['pool_name'] == pool_name and \
+ tgt_map['vol_name'] == vol_name:
+ # Already masked.
+ return None
+
+ node_acl_group = NodeACLGroup(tpg, ag_name)
+ if len(list(node_acl_group.wwns)) == 0:
+ # Non-exist access group means volume mapping status will not be
+ # stored. This should be considered as an error instead of sliently
+ # return.
+ raise TargetdError(
+ TargetdError.NOT_FOUND_ACCESS_GROUP, "Access group not found")
+
+ if h_lun_id is None:
+ # Find out next available host LUN ID
+ # Assuming max host LUN ID is LUN.MAX_LUN
+ free_h_lun_ids = set(range(LUN.MAX_LUN+1)) - \
+ set([int(x.mapped_lun) for x in tpg_lun.mapped_luns])
+ if len(free_h_lun_ids) == 0:
+ raise TargetdError(
+ TargetdError.NO_FREE_HOST_LUN_ID,
+ "All host LUN ID 0 ~ %d is in use" % LUN.MAX_LUN)
+ else:
+ h_lun_id = free_h_lun_ids.pop()
+
+ node_acl_group.mapped_lun_group(h_lun_id, tpg_lun)
+ RTSRoot().save_to_file()
diff --git a/targetd/utils.py b/targetd/utils.py
index 522f445..e2b82ae 100644
--- a/targetd/utils.py
+++ b/targetd/utils.py
@@ -42,6 +42,8 @@ class TargetdError(Exception):
NO_SUPPORT = -153
NAME_CONFLICT = -50
EXISTS_INITIATOR = -52
+ NO_FREE_HOST_LUN_ID = -1000
+ NOT_FOUND_ACCESS_GROUP = -200
def __init__(self, error_code, *args, **kwargs):
Exception.__init__(self, *args, **kwargs)
--
1.8.3.1