[targetcli] Initial import (#744340)
Andy Grover
grover at fedoraproject.org
Fri Nov 4 22:18:13 UTC 2011
commit 2b97aa1fee6414e0be23a4e45efc8d4e2487f12a
Author: Andy Grover <agrover at redhat.com>
Date: Fri Nov 4 15:17:44 2011 -0700
Initial import (#744340)
.gitignore | 1 +
...from-cli-welcome-msg.-Mention-help-is-ava.patch | 29 +
0002-bundle-lio-utils.patch | 3992 ++++++++++++++++++++
...scripts-aren-t-in-PATH-anymore-so-call-th.patch | 136 +
...ors-from-failure-to-set-device-attributes.patch | 26 +
0005-fix-spec_root-path.patch | 29 +
0006-add-docs.patch | 174 +
0007-all-start.patch | 35 +
sources | 1 +
targetcli-git-version.patch | 13 +
targetcli.service | 13 +
targetcli.spec | 92 +
12 files changed, 4541 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index e69de29..593a6e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/targetcli-1.99.2.gitb03ec79.tar.gz
diff --git a/0001-Remove-ads-from-cli-welcome-msg.-Mention-help-is-ava.patch b/0001-Remove-ads-from-cli-welcome-msg.-Mention-help-is-ava.patch
new file mode 100644
index 0000000..ee0691b
--- /dev/null
+++ b/0001-Remove-ads-from-cli-welcome-msg.-Mention-help-is-ava.patch
@@ -0,0 +1,29 @@
+From 786d5b84d653c019c391fa07e483947156a03e3d Mon Sep 17 00:00:00 2001
+From: Andy Grover <agrover at redhat.com>
+Date: Sat, 30 Jul 2011 16:02:43 -0700
+Subject: [PATCH 1/6] Remove ads from cli welcome msg. Mention help is available.
+
+Signed-off-by: Andy Grover <agrover at redhat.com>
+---
+ scripts/targetcli | 4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/scripts/targetcli b/scripts/targetcli
+index 8af0613..738f02c 100755
+--- a/scripts/targetcli
++++ b/scripts/targetcli
+@@ -58,9 +58,9 @@ def main():
+ shell = TargetCLI('~/.targetcli')
+ shell.con.epy_write('''
+ Welcome to the B{targetcli} shell::
+- Copyright (c) 2011 by RisingTide Systems LLC.
++ Copyright (c) 2011 by RisingTide Systems LLC.\n
++ For help on commands, type 'help'.
+
+- Visit us at U{http://www.risingtidesystems.com}.
+ ''')
+ shell.con.display('')
+ if not is_root:
+--
+1.7.1
+
diff --git a/0002-bundle-lio-utils.patch b/0002-bundle-lio-utils.patch
new file mode 100644
index 0000000..4e497b5
--- /dev/null
+++ b/0002-bundle-lio-utils.patch
@@ -0,0 +1,3992 @@
+From c55d9206e4f783d25ceb574ba07c40a83daf00a3 Mon Sep 17 00:00:00 2001
+From: Andy Grover <agrover at redhat.com>
+Date: Thu, 18 Aug 2011 11:19:35 -0700
+Subject: [PATCH 2/6] bundle lio-utils
+
+lio-utils is used just for saving state. It should be going away
+shortly, so just bundle it for now.
+
+Signed-off-by: Andy Grover <agrover at redhat.com>
+---
+ targetcli/lio_dump.py | 265 +++++++++
+ targetcli/lio_node.py | 1349 ++++++++++++++++++++++++++++++++++++++++++++++
+ targetcli/tcm_dump.py | 366 +++++++++++++
+ targetcli/tcm_fabric.py | 532 ++++++++++++++++++
+ targetcli/tcm_fileio.py | 83 +++
+ targetcli/tcm_iblock.py | 86 +++
+ targetcli/tcm_loop.py | 242 +++++++++
+ targetcli/tcm_node.py | 737 +++++++++++++++++++++++++
+ targetcli/tcm_pscsi.py | 184 +++++++
+ targetcli/tcm_ramdisk.py | 53 ++
+ 10 files changed, 3897 insertions(+), 0 deletions(-)
+ create mode 100644 targetcli/lio_dump.py
+ create mode 100644 targetcli/lio_node.py
+ create mode 100644 targetcli/tcm_dump.py
+ create mode 100644 targetcli/tcm_fabric.py
+ create mode 100644 targetcli/tcm_fileio.py
+ create mode 100644 targetcli/tcm_iblock.py
+ create mode 100644 targetcli/tcm_loop.py
+ create mode 100644 targetcli/tcm_node.py
+ create mode 100644 targetcli/tcm_pscsi.py
+ create mode 100644 targetcli/tcm_ramdisk.py
+
+diff --git a/targetcli/lio_dump.py b/targetcli/lio_dump.py
+new file mode 100644
+index 0000000..81c5104
+--- /dev/null
++++ b/targetcli/lio_dump.py
+@@ -0,0 +1,265 @@
++import os, sys
++import subprocess as sub
++import string
++import re
++import datetime, time
++from optparse import OptionParser
++
++lio_root = "/sys/kernel/config/target/iscsi"
++
++def lio_target_configfs_dump(option, opt_str, value, parser):
++
++ if not os.path.isdir(lio_root):
++ print "Unable to access lio_root: " + lio_root
++ sys.exit(1)
++
++ iqn_root = os.listdir(lio_root)
++
++ # This will load up iscsi_target_mod.ko
++ print "mkdir " + lio_root
++
++ print "#### iSCSI Discovery authentication information"
++ auth_dir = lio_root + "/discovery_auth"
++ if os.path.isdir(auth_dir) == True:
++ for auth in os.listdir(auth_dir):
++ if auth == "authenticate_target":
++ continue
++
++ auth_file = auth_dir + "/" + auth
++ p = os.open(auth_file, 0)
++ value = os.read(p, 256)
++ ret = value.isspace()
++ if ret:
++ os.close(p)
++ continue
++ print "echo -n " + value.rstrip() + " > " + auth_file
++ os.close(p)
++
++ iqn_root = os.listdir(lio_root)
++
++ # Loop through LIO-Target IQN list
++ for iqn in iqn_root:
++ if iqn == "lio_version":
++ continue
++ if iqn == "discovery_auth":
++ continue
++
++ # Loop through LIO-Target IQN+TPGT list
++ tpg_root = os.listdir(lio_root + "/" + iqn);
++ for tpgt_tmp in tpg_root:
++ if tpgt_tmp == "fabric_statistics":
++ continue
++
++ tpgt_tmp2 = tpgt_tmp.split('_')
++ tpgt = tpgt_tmp2[1]
++
++ print "#### Network portals for iSCSI Target Portal Group"
++ np_root = os.listdir(lio_root + "/" + iqn + "/tpgt_" + tpgt + "/np")
++ for np in np_root:
++ print "mkdir -p " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/np/" + np
++
++ print "#### iSCSI Target Ports"
++ lun_root = os.listdir(lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun")
++ for lun_tmp in lun_root:
++ lun_tmp2 = lun_tmp.split('_')
++ lun = lun_tmp2[1]
++
++ lun_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ print "mkdir -p " + lun_dir
++
++ port_root = os.listdir(lun_dir)
++ for port in port_root:
++ if port == "alua_tg_pt_gp":
++ continue
++ if port == "alua_tg_pt_offline":
++ continue
++ if port == "alua_tg_pt_status":
++ continue
++ if port == "alua_tg_pt_write_md":
++ continue
++
++ if not os.path.islink(lun_dir + "/" + port):
++ continue
++
++ port_link = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun + "/" + port
++ sourcelink = os.readlink(port_link)
++ sourcelink2 = os.path.join(os.path.dirname(port_link), sourcelink)
++ print "ln -s " + sourcelink2 + " " + port_link
++
++ # Dump ALUA Target Port Group
++ tg_pt_gp_file = lun_dir + "/alua_tg_pt_gp"
++ p = os.open(tg_pt_gp_file, 0)
++ try:
++ value = os.read(p, 512)
++ except:
++ os.close(p)
++ continue
++ os.close(p)
++ if value:
++ tg_pt_gp_tmp = value.split('\n')
++ tg_pt_gp_out = tg_pt_gp_tmp[0]
++ off = tg_pt_gp_out.index('Alias: ')
++ off += 7 # Skip over "Alias: "
++ tg_pt_gp_name = tg_pt_gp_out[off:]
++ # Only need to dump if LIO-Target Port is NOT partof
++ # the 'default_tg_pt_gp'
++ if not re.search(tg_pt_gp_name, 'default_tg_pt_gp'):
++ print "#### ALUA Target Port Group"
++ print "echo " + tg_pt_gp_name + " > " + tg_pt_gp_file
++
++ print "lio_node --aluasecmd " + iqn + " " + tpgt + " " + lun
++
++ # Dump values of iscsi/iqn/tpgt/attrib/
++ print "#### Attributes for iSCSI Target Portal Group"
++ attrib_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/attrib/"
++ attrib_root = os.listdir(attrib_dir)
++ for attrib in attrib_root:
++ attrib_file = attrib_dir + attrib
++ p = os.open(attrib_file, 0)
++ value = os.read(p, 16)
++ print "echo " + value.rstrip() + " > " + attrib_file
++ os.close(p)
++
++ # Dump values for iscsi/iqn/tpgt/param
++ print "#### Parameters for iSCSI Target Portal Group"
++ param_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/param/"
++ param_root = os.listdir(param_dir)
++ for param in param_root:
++ param_file = param_dir + param
++ p = os.open(param_file, 0)
++ value = os.read(p, 256)
++ print "echo \"" + value.rstrip() + "\" > " + param_file
++ os.close(p)
++
++ # Dump iSCSI Initiator Node ACLs from iscsi/iqn/tpgt/acls
++ print "#### iSCSI Initiator ACLs for iSCSI Target Portal Group"
++ nacl_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/"
++ nacl_root = os.listdir(nacl_dir)
++ for nacl in nacl_root:
++ print "mkdir -p " + nacl_dir + nacl
++ tcq_depth_file = nacl_dir + nacl + "/cmdsn_depth"
++ p = os.open(tcq_depth_file, 0)
++ value = os.read(p, 8)
++ print "echo " + value.rstrip() + " > " + tcq_depth_file
++ os.close(p)
++
++ # Dump iSCSI Initiator ACL authentication info from iscsi/iqn/tpgt/acls/$INITIATOR/auth
++ print "#### iSCSI Initiator ACL authentication information"
++ auth_dir = nacl_dir + nacl + "/auth"
++ for auth in os.listdir(auth_dir):
++ if auth == "authenticate_target":
++ continue
++ auth_file = auth_dir + "/" + auth
++ p = os.open(auth_file, 0)
++ value = os.read(p, 256)
++ ret = value.isspace()
++ if ret:
++ os.close(p)
++ continue
++ print "echo -n " + value.rstrip() + " > " + auth_file
++ os.close(p)
++
++ # Dump iSCSI Initiator ACL TPG attributes from iscsi/iqn/tpgt/acls/$INITIATOR/attrib
++ print "#### iSCSI Initiator ACL TPG attributes"
++ nacl_attrib_dir = nacl_dir + nacl + "/attrib"
++ for nacl_attrib in os.listdir(nacl_attrib_dir):
++ nacl_attrib_file = nacl_attrib_dir + "/" + nacl_attrib
++ p = os.open(nacl_attrib_file, 0)
++ value = os.read(p, 8)
++ print "echo " + value.rstrip() + " > " + nacl_attrib_file
++ os.close(p)
++
++ # Dump iSCSI Initiator LUN ACLs from iscsi/iqn/tpgt/acls/$INITIATOR/lun
++ print "#### iSCSI Initiator LUN ACLs for iSCSI Target Portal Group"
++ lun_acl_dir = nacl_dir + nacl
++ for lun_acl in os.listdir(lun_acl_dir):
++ ret = re.search('lun_', lun_acl)
++ if not ret:
++ continue
++ lun_link_dir = nacl_dir + nacl + "/" + lun_acl
++ print "mkdir -p " + lun_link_dir
++
++ for lun_acl_link in os.listdir(lun_link_dir):
++ if lun_acl_link == "write_protect":
++ p = os.open(lun_link_dir + "/write_protect", 0)
++ value = os.read(p, 4)
++ print "echo " + value.rstrip() + " > " + lun_link_dir + "/write_protect"
++ os.close(p)
++ continue
++
++ if not os.path.islink(lun_link_dir + "/" + lun_acl_link):
++ continue
++
++ sourcelink = os.readlink(lun_link_dir + "/" + lun_acl_link)
++ sourcelink2 = os.path.join(os.path.dirname(lun_link_dir + "/" + lun_acl_link), sourcelink)
++ print "ln -s " + sourcelink2 + " " + lun_link_dir + "/" + lun_acl_link
++
++ # Dump value of iscsi/iqn/tpgt/enable
++ print "#### Trigger to enable iSCSI Target Portal Group"
++ enable_file = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/enable"
++ p = os.open(enable_file, 0)
++ value = os.read(p, 1)
++ print "echo " + value.rstrip() + " > " + enable_file
++ os.close(p)
++
++# lio_target_del_iqn(None, None, iqn, None)
++
++ return
++
++def lio_backup_to_file(option, opt_str, value, parser):
++ now = str(value)
++
++ if not os.path.isdir(lio_root):
++ print "Unable to access lio_root: " + lio_root
++ sys.exit(1)
++
++ backup_dir = "/etc/target/backup"
++ if not os.path.isdir(backup_dir):
++ op = "mkdir " + backup_dir
++ ret = os.system(op)
++ if ret:
++ print "Unable to open backup_dir"
++ sys.exit(1)
++
++ op = "lio_dump --stdout"
++ p = sub.Popen(op, shell=True, stdout=sub.PIPE).stdout
++ if not p:
++ print "Unable to dump LIO-Target/ConfigFS running state"
++ sys.exit(1)
++
++ print "Making backup of LIO-Target/ConfigFS with timestamp: " + now
++ backup_file = backup_dir + "/lio_backup-" + now + ".sh"
++ if os.path.isfile(backup_file):
++ print "LIO-Target backup_file: " + backup_file + "already exists, exiting"
++ p.close()
++ sys.exit(1)
++
++ back = open(backup_file, 'w')
++
++ line = p.readline()
++ while line:
++ print >>back, line.rstrip()
++ line = p.readline()
++
++ p.close()
++ back.close()
++ return backup_file
++
++def main():
++
++ parser = OptionParser()
++ parser.add_option("--s","--stdout", action="callback", callback=lio_target_configfs_dump, nargs=0,
++ help="Dump running LIO-Target/ConfigFS syntax to STDOUT")
++ parser.add_option("--t", "--tofile", action="callback", callback=lio_backup_to_file, nargs=1,
++ type="string", dest="DATE_TIME", help="Backup running LIO-Target/ConfigFS syntax to /etc/target/backup/lio_backup-<DATE_TIME>.sh")
++
++ (options, args) = parser.parse_args()
++ if len(sys.argv) == 1:
++ parser.print_help()
++ sys.exit(0)
++ elif not re.search('--', sys.argv[1]):
++ print "Unknown CLI option: " + sys.argv[1]
++ sys.exit(1)
++
++if __name__ == "__main__":
++ main()
+diff --git a/targetcli/lio_node.py b/targetcli/lio_node.py
+new file mode 100644
+index 0000000..de2c1e3
+--- /dev/null
++++ b/targetcli/lio_node.py
+@@ -0,0 +1,1349 @@
++import os, sys
++import subprocess as sub
++import string
++import re
++from optparse import OptionParser
++
++tcm_root = "/sys/kernel/config/target/core"
++lio_root = "/sys/kernel/config/target/iscsi"
++alua_secondary_md_dir = "/var/target/alua/iSCSI/"
++
++def lio_err(msg):
++ print msg
++ sys.exit(1)
++
++def lio_alua_check_secondary_md(iqn, tpgt):
++ alua_sec_md_path = alua_secondary_md_dir + iqn + "+" + tpgt + "/"
++ if os.path.isdir(alua_sec_md_path) == False:
++ mkdir_op = "mkdir -p " + alua_sec_md_path
++ ret = os.system(mkdir_op)
++ if ret:
++ lio_err("Unable to create secondary ALUA MD directory: " + alua_sec_md_path)
++
++ return
++
++def lio_alua_delete_secondary_md(iqn, tpgt):
++ alua_sec_md_path = alua_secondary_md_dir + iqn + "+" + tpgt + "/"
++ if os.path.isdir(alua_sec_md_path) == False:
++ return
++
++ rm_op = "rm -rf " + alua_sec_md_path
++ ret = os.system(rm_op)
++ if ret:
++ lio_err("Unable to remove secondary ALUA MD directory: " + alua_sec_md_path)
++
++ return
++
++def lio_alua_delete_secondary_md_port(iqn, tpgt, lun):
++
++ alua_sec_md_file = alua_secondary_md_dir + iqn + "+" + tpgt + "/lun_" + lun
++ if os.path.isfile(alua_sec_md_file) == False:
++ return
++
++ rm_op = "rm -rf "+ alua_sec_md_file
++ ret = os.system(rm_op)
++ if ret:
++ lio_err("Unable to delete ALUA secondary metadata file: " + alua_sec_md_file)
++
++ return
++
++def lio_alua_set_secondary_write_md(iqn, tpgt, lun):
++ alua_write_md_file = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun + "/alua_tg_pt_write_md"
++ if os.path.isfile(alua_write_md_file) == False:
++ return
++
++ p = open(alua_write_md_file, 'w')
++ if not p:
++ lio_err("Unable to open: " + alua_write_md_file)
++
++ ret = p.write("1")
++ if ret:
++ lio_err("Unable to enable writeable ALUA secondary metadata for " + alua_write_md_file)
++
++ p.close()
++ return
++
++def lio_alua_process_secondary_md(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ lun = str(value[2]);
++
++ alua_sec_md_file = alua_secondary_md_dir + iqn + "+" + tpgt + "/lun_" + lun
++ if os.path.isfile(alua_sec_md_file) == False:
++ # Use --aluasecmd as a chance to make sure the directory for this
++ # LIO-Target endpoint (iqn+tpgt) exists..
++ lio_alua_check_secondary_md(iqn, tpgt)
++ lio_alua_set_secondary_write_md(iqn, tpgt, lun)
++ lio_err("Unable to locate ALUA secondary metadata file: " + alua_sec_md_file)
++ return
++
++# print "Using alua_sec_md_file: " + alua_sec_md_file
++ alua_sec_cfs_path = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++# print "Using alua_sec_cfs_path: " + alua_sec_cfs_path
++
++ p = open(alua_sec_md_file, 'rU')
++ if not p:
++ print "Unable to process ALUA secondary metadata for: " + alua_sec_md_file
++
++ line = p.readline()
++ while line:
++ buf = line.rstrip()
++
++ if re.search('alua_tg_pt_offline=', buf):
++ alua_tg_pt_offline = buf[19:]
++# print "Extracted alua_tg_pt_offline: " + alua_tg_pt_offline
++ cfs = open(alua_sec_cfs_path + "/alua_tg_pt_offline", 'w')
++ if not cfs:
++ p.close()
++ lio_err("Unable to open " + alua_sec_cfs_path + "/alua_tg_pt_offline")
++
++ ret = cfs.write(alua_tg_pt_offline)
++ cfs.close()
++ if ret:
++ p.close()
++ lio_err("Unable to write " + alua_sec_cfs_path + "/alua_tg_pt_offline")
++
++ elif re.search('alua_tg_pt_status=', buf):
++ alua_tg_pt_status = buf[18:]
++# print "Extracted alua_tg_pt_status: " + alua_tg_pt_status
++ cfs = open(alua_sec_cfs_path + "/alua_tg_pt_status", 'w')
++ if not cfs:
++ p.close()
++ lio_err("Unable to open " + alua_sec_cfs_path + "/alua_tg_pt_status")
++
++ ret = cfs.write(alua_tg_pt_status)
++ cfs.close()
++ if ret:
++ p.close()
++ lio_err("Unable to write " + alua_sec_cfs_path + "/alua_tg_pt_status")
++
++ line = p.readline()
++
++ p.close()
++ # Now enable the alua_tg_pt_write_md bit to allow for new updates
++ # to ALUA secondary metadata in struct file for this port
++ lio_alua_set_secondary_write_md(iqn, tpgt, lun)
++ return
++
++def __lio_target_del_iqn(option, opt_str, value, parser, delete_tpg_md):
++ iqn = str(value);
++ iqn = iqn.lower();
++
++# Loop through LIO-Target IQN+TPGT list
++ tpg_root = os.listdir(lio_root + "/" + iqn);
++ for tpgt_tmp in tpg_root:
++ if tpgt_tmp == "fabric_statistics":
++ continue
++
++ tpgt_tmp2 = tpgt_tmp.split('_')
++ tpgt = tpgt_tmp2[1]
++
++ tpg_val = [iqn,tpgt]
++ if delete_tpg_md == 1:
++ lio_target_del_tpg(None, None, tpg_val, None)
++ else:
++ __lio_target_del_tpg(None, None, tpg_val, None, 0)
++
++ rmdir_op = "rmdir " + lio_root + "/" + iqn
++# print "rmdir_op: " + rmdir_op
++ ret = os.system(rmdir_op)
++ if not ret:
++ print "Successfully released iSCSI Target Endpoint IQN: " + iqn
++ else:
++ lio_err("Unable to release iSCSI Target Endpoint IQN: " + iqn)
++
++ return
++
++def lio_target_del_iqn(option, opt_str, value, parser):
++ iqn = str(value);
++ iqn = iqn.lower();
++
++ iqn_dir = lio_root + "/" + iqn
++ if os.path.isdir(iqn_dir) == False:
++ lio_err("Unable to locate iSCSI Target IQN: " + iqn_dir)
++
++ # Passing 1 for delete_tpg_md here means lio_target_del_tpg()
++ # with lio_alua_delete_secondary_md() will get called to delete
++ # all of the secondary ALUA directories for the LIO-Target endpoint
++ # when an explict --deliqn is called.
++ __lio_target_del_iqn(option, opt_str, value, parser, 1)
++
++ return
++
++def __lio_target_del_tpg(option, opt_str, value, parser, delete_tpg_md):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ # This will set TPG Status to INACTIVE force all of the iSCSI sessions for this
++ # tiqn+tpgt tuple to be released.
++ disable_op = "echo 0 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/enable"
++ ret = os.system(disable_op)
++ if ret:
++ print "Unable to disable TPG: " + iqn + " TPGT: " + tpgt
++
++ np_root = os.listdir(lio_root + "/" + iqn + "/tpgt_" + tpgt + "/np")
++ for np in np_root:
++ np_val = [iqn,tpgt,np]
++
++ lio_target_del_np(None, None, np_val, None)
++
++ nacl_root = os.listdir(lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls")
++ for nacl in nacl_root:
++ nacl_val = [iqn,tpgt,nacl]
++
++ lio_target_del_nodeacl(None, None, nacl_val, None)
++
++ lun_root = os.listdir(lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun")
++ for lun_tmp in lun_root:
++ lun_tmp2 = lun_tmp.split('_')
++ lun = lun_tmp2[1]
++
++ lun_val = [iqn,tpgt,lun]
++ if delete_tpg_md == 1:
++ lio_target_del_port(None, None, lun_val, None)
++ else:
++ __lio_target_del_port(None, None, lun_val, None)
++
++
++ rmdir_op = "rmdir " + lio_root + "/" + iqn + "/tpgt_" + tpgt
++# print "rmdir_op: " + rmdir_op
++ ret = os.system(rmdir_op)
++ if not ret:
++ print "Successfully released iSCSI Target Portal Group: " + iqn + " TPGT: " + tpgt
++ else:
++ lio_err("Unable to release iSCSI Target Portal Group: " + iqn + " TPGT: " + tpgt)
++
++ return
++
++def lio_target_del_tpg(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ tpgt_file = lio_root + "/" + iqn + "/tpgt_" + tpgt
++ if os.path.isdir(tpgt_file) == False:
++ lio_err("iSCSI Target Port Group: " + tpgt_file + " does not exist")
++
++ __lio_target_del_tpg(option, opt_str, value, parser, 1)
++ # Delete the ALUA secondary metadata directory on explict --deltpg or
++ # called from --deliqn
++ lio_alua_delete_secondary_md(iqn, tpgt)
++ return
++
++def lio_target_add_np(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ np = str(value[2]);
++
++ # Append default iSCSI Port is non is given.
++ if re.search(']', np)and not re.search(']:', np):
++ np = np + ":3260"
++ elif re.search(':\\Z', np):
++ np = np + "3260"
++ elif not re.search(':', np):
++ np = np + ":3260"
++
++ # Extract the iSCSI port and make sure it is a valid u16 value
++ if re.search(']:', np):
++ off = np.index(']:')
++ off += 2
++ port = int(np[off:])
++ else:
++ off = np.index(':')
++ off += 1
++ port = int(np[off:])
++
++ if port == 0 or port > 65535:
++ lio_err("Illegal port value: " + str(port) + " for iSCSI network portal")
++
++ mkdir_op = "mkdir -p " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/np/" + np
++# print "mkdir_op: " + mkdir_op
++ ret = os.system(mkdir_op)
++ if not ret:
++ print "Successfully created network portal: " + np + " created " + iqn + " TPGT: " + tpgt
++ lio_alua_check_secondary_md(iqn, tpgt)
++ else:
++ lio_err("Unable to create network portal: " + np + " created " + iqn + " TPGT: " + tpgt)
++
++ return
++
++def lio_target_del_np(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ np = str(value[2]);
++
++ rmdir_op = "rmdir " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/np/" + np
++# print "rmdir_op: " + rmdir_op
++ ret = os.system(rmdir_op)
++ if not ret:
++ print "Successfully released network portal: " + np + " created " + iqn + " TPGT: " + tpgt
++ else:
++ lio_err("Unable to release network portal: " + np + " created " + iqn + " TPGT: " + tpgt)
++
++ return
++
++def lio_target_add_port(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ lun = str(value[2]);
++ port_name = str(value[3]);
++ tcm_obj = str(value[4]);
++
++ lun_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ if os.path.isdir(lun_dir):
++ lio_err("iSCSI Target Logical Unit ConfigFS directory already exists")
++
++ mkdir_op = "mkdir -p " + lun_dir
++# print "mkdir_op: " + mkdir_op
++ ret = os.system(mkdir_op)
++ if ret:
++ lio_err("Unable to create iSCSI Target Logical Unit ConfigFS directory")
++
++ port_src = tcm_root + "/" + tcm_obj
++ port_dst = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun + "/" + port_name
++ link_op = "ln -s " + port_src + " " + port_dst
++# print "link_op: " + link_op
++ ret = os.system(link_op)
++ if not ret:
++ print "Successfully created iSCSI Target Logical Unit"
++ lio_alua_check_secondary_md(iqn, tpgt)
++ lio_alua_set_secondary_write_md(iqn, tpgt, lun)
++ else:
++ os.rmdir(lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun)
++ lio_err("Unable to create iSCSI Target Logical Unit symlink")
++
++ return
++
++def lio_target_add_tpg(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ tpg_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt
++ if os.path.isdir(tpg_dir):
++ lio_err("iSCSI Target Portal Group directory already exists")
++
++ mkdir_op = "mkdir -p " + tpg_dir
++ ret = os.system(mkdir_op)
++ if ret:
++ lio_err("Unable to create iSCSI Target Portal Group ConfigFS directory")
++ else:
++ print "Successfully created iSCSI Target Portal Group"
++ lio_alua_check_secondary_md(iqn, tpgt)
++
++ return
++
++def __lio_target_del_port(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ lun = str(value[2]);
++
++ lun_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ port_root = os.listdir(lun_dir)
++
++ for port in port_root:
++ if port == "alua_tg_pt_gp":
++ continue
++ if port == "alua_tg_pt_offline":
++ continue
++ if port == "alua_tg_pt_status":
++ continue
++ if port == "alua_tg_pt_write_md":
++ continue
++
++ if not os.path.islink(lun_dir + "/" + port):
++ continue
++
++ unlink_op = "unlink " + lun_dir + "/" + port
++# print "del_portunlink_op: " + unlink_op
++ ret = os.system(unlink_op)
++ if ret:
++ lio_err("Unable to unlink iSCSI Target Logical Unit")
++
++ rmdir_op= "rmdir " + lun_dir
++# print "del_port rmdir_op: " + rmdir_op
++ ret = os.system(rmdir_op);
++ if not ret:
++ print "Successfully deleted iSCSI Target Logical Unit"
++ else:
++ lio_err("Unable to rmdir iSCSI Target Logical Unit configfs directory")
++
++ return
++
++def lio_target_del_port(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ lun = str(value[2]);
++
++ lun_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ if os.path.isdir(lun_dir) == False:
++ lio_err("LIO-Target Port/LUN directory: " + lun_dir + " does not exist")
++
++ __lio_target_del_port(option, opt_str, value, parser)
++ # Delete the ALUA secondary metadata file for this Port/LUN
++ # during an explict --dellun
++ lio_alua_delete_secondary_md_port(iqn, tpgt, lun)
++
++ return
++
++def lio_target_tpg_disableauth(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ enable_op = "echo 0 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/attrib/authentication"
++ ret = os.system(enable_op)
++ if ret:
++ lio_err("Unable to disable iSCSI Authentication on iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully disabled iSCSI Authentication on iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_tpg_demomode(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ enable_op = "echo 1 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/attrib/generate_node_acls"
++ ret = os.system(enable_op)
++ if ret:
++ lio_err("Unable to disable Initiator ACL mode (Enable DemoMode) on iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully disabled Initiator ACL mode (Enabled DemoMode) on iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_disable_lunwp(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++ mapped_lun = str(value[3]);
++
++ disable_op = "echo 0 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/lun_" + mapped_lun + "/write_protect"
++ ret = os.system(disable_op)
++ if ret:
++ lio_err("Unable to disable WriteProtect for Mapped LUN: " + mapped_lun + " for " + initiator_iqn + " on iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully disabled WRITE PROTECT for Mapped LUN: " + mapped_lun + " for " + initiator_iqn + " on iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_enable_auth(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ enable_op = "echo 1 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/attrib/authentication"
++ ret = os.system(enable_op)
++ if ret:
++ lio_err("Unable to enable iSCSI Authentication on iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully enabled iSCSI Authentication on iSCSI Target Portal Group: " + iqn + " " + tpgt
++ return
++
++def lio_target_enable_lunwp(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++ mapped_lun = str(value[3]);
++
++ enable_op = "echo 1 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/lun_" + mapped_lun + "/write_protect"
++ ret = os.system(enable_op)
++ if ret:
++ lio_err("Unable to enable WriteProtect for Mapped LUN: " + mapped_lun + " for " + initiator_iqn + " on iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully enabled WRITE PROTECT for Mapped LUN: " + mapped_lun + " for " + initiator_iqn + " on iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_enable_tpg(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ enable_op = "echo 1 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/enable"
++ ret = os.system(enable_op)
++ if ret:
++ lio_err("Unable to enable iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully enabled iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_disable_tpg(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ disable_op = "echo 0 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/enable"
++ ret = os.system(disable_op)
++ if ret:
++ lio_err("Unable to disable iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully disabled iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_enableaclmode(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ enable_op = "echo 0 > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/attrib/generate_node_acls"
++ ret = os.system(enable_op)
++ if ret:
++ lio_err("Unable to enable Initiator ACL mode (Disabled DemoMode) on iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully enabled Initiator ACL mode (Disabled DemoMode) on iSCSI Target Portal Group: " + iqn + " " + tpgt
++ return
++
++
++def lio_target_add_lunacl(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++ tpg_lun = str(value[3]);
++ mapped_lun = str(value[4]);
++
++ mkdir_op = "mkdir -p " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/lun_" + mapped_lun
++ ret = os.system(mkdir_op)
++ if ret:
++ lio_err("Unable to add iSCSI Initiator Mapped LUN: " + mapped_lun + " ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++
++ addlunacl_op = "ln -s " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + tpg_lun + " " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/lun_" + mapped_lun + "/lio_lun"
++
++ ret = os.system(addlunacl_op)
++ if ret:
++ lio_err("Unable to add iSCSI Initiator Mapped LUN: " + mapped_lun + " ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully added iSCSI Initiator Mapped LUN: " + mapped_lun + " ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt
++ lio_alua_check_secondary_md(iqn, tpgt)
++
++ return
++
++def lio_target_del_lunacl(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++ mapped_lun = str(value[3]);
++
++ lun_link_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/lun_" + mapped_lun
++ for lun_acl_link in os.listdir(lun_link_dir):
++ if lun_acl_link == "write_protect":
++ continue
++
++ if not os.path.islink(lun_link_dir + "/" + lun_acl_link):
++ continue;
++
++ unlink_op = "unlink " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/lun_" + mapped_lun + "/" + lun_acl_link
++# print "unlink_op: " + unlink_op
++ ret = os.system(unlink_op)
++ if ret:
++ lio_err("Unable to unlink iSCSI Initiator Mapped LUN: " + mapped_lun + " ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++
++ dellunacl_op = "rmdir " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/lun_" + mapped_lun
++ ret = os.system(dellunacl_op)
++ if ret:
++ lio_err("Unable to delete iSCSI Initiator Mapped LUN: " + mapped_lun + " ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully deleted iSCSI Initiator Mapped LUN: " + mapped_lun + " ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_add_nodeacl(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++
++ addnodeacl_op = "mkdir -p " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn
++ ret = os.system(addnodeacl_op)
++ if ret:
++ lio_err("Unable to add iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully added iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt
++ lio_alua_check_secondary_md(iqn, tpgt)
++
++ return
++
++def lio_target_del_nodeacl(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++
++ nacl_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn
++ lun_acl_root = os.listdir(nacl_dir)
++ for lun_acl in lun_acl_root:
++ ret = re.search('lun_', lun_acl)
++ if not ret:
++ continue
++ lun_delacl_val = [iqn,tpgt,initiator_iqn,lun_acl[4:]]
++ lio_target_del_lunacl(None, None, lun_delacl_val, None)
++
++ delnodeacl_op = "rmdir " + nacl_dir
++ ret = os.system(delnodeacl_op)
++ if ret:
++ lio_err("Unable to delete iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully deleted iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_set_chap_auth(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++ user = str(value[3]);
++ password = str(value[4]);
++
++ auth_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/auth/"
++
++ if not os.path.isdir(auth_dir):
++ lio_err("iSCSI Initiator ACL " + initiator_iqn + " does not exist for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++
++ setuser_op = "echo -n " + user + " > " + auth_dir + "/userid"
++ ret = os.system(setuser_op)
++ if ret:
++ lio_err("Unable to set CHAP username for iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++
++ setpassword_op = "echo -n " + password + " > " + auth_dir + "/password"
++ ret = os.system(setpassword_op)
++ if ret:
++ lio_err("Unable to set CHAP password for iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully set CHAP authentication for iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_set_chap_mutual_auth(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++ user_mutual = str(value[3]);
++ password_mutual = str(value[4]);
++
++ auth_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/auth/"
++
++ if not os.path.isdir(auth_dir):
++ lio_err("iSCSI Initiator ACL " + initiator_iqn + " does not exist for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++
++ setuser_op = "echo -n " + user_mutual + " > " + auth_dir + "/userid_mutual"
++ ret = os.system(setuser_op)
++ if ret:
++ lio_err("Unable to set mutual CHAP username for iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++
++ setpassword_op = "echo -n " + password_mutual + " > " + auth_dir + "/password_mutual"
++ ret = os.system(setpassword_op)
++ if ret:
++ lio_err("Unable to set mutual CHAP password for iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully set mutual CHAP authentication for iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_set_chap_discovery_auth(option, opt_str, value, parser):
++ user = str(value[0]);
++ password = str(value[1]);
++
++ auth_dir = lio_root + "/discovery_auth"
++ if not os.path.isdir(auth_dir):
++ lio_err("iSCSI Discovery Authentication directory " + auth_dir + " does not exist")
++
++ setuser_op = "echo -n " + user + " > " + auth_dir + "/userid"
++ ret = os.system(setuser_op)
++ if ret:
++ lio_err("Unable to set CHAP username for iSCSI Discovery Authentication")
++
++ setpassword_op = "echo -n " + password + " > " + auth_dir + "/password"
++ ret = os.system(setpassword_op)
++ if ret:
++ lio_err("Unable to set CHAP password for iSCSI Discovery Authentication")
++ else:
++ print "Successfully set CHAP authentication for iSCSI Discovery Authentication"
++
++ return
++
++def lio_target_set_chap_mutual_discovery_auth(option, opt_str, value, parser):
++ user_mutual = str(value[0]);
++ password_mutual = str(value[1]);
++
++ auth_dir = lio_root + "/discovery_auth"
++ if not os.path.isdir(auth_dir):
++ lio_err("iSCSI Discovery Authentication directory " + auth_dir + " does not exist")
++
++ setuser_op = "echo -n " + user_mutual + " > " + auth_dir + "/userid_mutual"
++ ret = os.system(setuser_op)
++ if ret:
++ lio_err("Unable to set mutual CHAP username for iSCSI Discovery Authentication")
++
++ setpassword_op = "echo -n " + password_mutual + " > " + auth_dir + "/password_mutual"
++ ret = os.system(setpassword_op)
++ if ret:
++ lio_err("Unable to set mutual CHAP password for iSCSI Discovery Authentication")
++ else:
++ print "Successfully set mutual CHAP authentication for iSCSI Discovery Authentication"
++
++ return
++
++def lio_target_set_enforce_discovery_auth(option, opt_str, value, parser):
++ value = str(value);
++
++ da_attr = lio_root + "/discovery_auth/enforce_discovery_auth"
++ if not os.path.isfile(da_attr):
++ lio_err("iSCSI Discovery Authentication directory does not exist")
++
++ da_op = "echo " + value + " > " + da_attr;
++ ret = os.system(da_op)
++ if ret:
++ lio_err("Unable to set da_attr: " + da_attr)
++
++ if value == "1":
++ print "Successfully enabled iSCSI Discovery Authentication enforcement"
++ else:
++ print "Successfully disabled iSCSI Discovery Authentication enforcement"
++
++ return
++
++
++def lio_target_set_node_tcq(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++ depth = str(value[3]);
++
++ setnodetcq_op = "echo " + depth + " > " + lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/cmdsn_depth"
++ ret = os.system(setnodetcq_op)
++ if ret:
++ lio_err("Unable to set TCQ: " + depth + " for iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++ else:
++ print "Successfully set TCQ: " + depth + " for iSCSI Initaitor ACL " + initiator_iqn + " for iSCSI Target Portal Group: " + iqn + " " + tpgt
++
++ return
++
++def lio_target_alua_set_tgptgp(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ lun = str(value[2]);
++ tg_pt_gp_name = str(value[3])
++
++ lun_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ if not os.path.isdir(lun_dir):
++ lio_err("LIO-Target Port/LUN: " + lun + " does not exist on: " + iqn + " " + tpgt)
++
++ set_tp_pt_gp_op = "echo " + tg_pt_gp_name + " > " + lun_dir + "/alua_tg_pt_gp"
++ ret = os.system(set_tp_pt_gp_op)
++ if ret:
++ lio_err("Unable to set ALUA Target Port Group: " + tg_pt_gp_name + " for LUN: " + lun + " on " + iqn + " " + tpgt)
++ else:
++ print "Successfully set ALUA Target Port Group: " + tg_pt_gp_name + " for LUN: " + lun + " on " + iqn + " " + tpgt
++
++ return
++
++def lio_target_alua_set_tgpt_offline(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ lun = str(value[2]);
++
++ lun_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ if not os.path.isdir(lun_dir):
++ lio_err("LIO-Target Port/LUN: " + lun + " does not exist on: " + iqn + " " + tpgt)
++
++ set_tg_pt_gp_offline_op = "echo 1 > " + lun_dir + "/alua_tg_pt_offline"
++ ret = os.system(set_tg_pt_gp_offline_op)
++ if ret:
++ lio_err("Unable to set ALUA secondary state OFFLINE bit for LUN: " + lun + " on " + iqn + " " + tpgt)
++ else:
++ print "Successfully set ALUA secondary state OFFLINE for LUN: " + lun + " on " + iqn + " " + tpgt
++
++ return
++
++def lio_target_alua_clear_tgpt_offline(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ lun = str(value[2]);
++
++ lun_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ if not os.path.isdir(lun_dir):
++ lio_err("LIO-Target Port/LUN: " + lun + " does not exist on: " + iqn + " " + tpgt)
++
++ set_tg_pt_gp_offline_op = "echo 0 > " + lun_dir + "/alua_tg_pt_offline"
++ ret = os.system(set_tg_pt_gp_offline_op)
++ if ret:
++ lio_err("Unable to clear ALUA secondary state OFFLINE for LUN: " + lun + " on " + iqn + " " + tpgt)
++ else:
++ print "Successfully cleared ALUA secondary state OFFLINE for LUN: " + lun + " on " + iqn + " " + tpgt
++ return
++
++def lio_target_show_chap_auth(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++
++ auth_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/auth/"
++
++ if not os.path.isdir(auth_dir):
++ lio_err("iSCSI Initiator ACL " + initiator_iqn + " does not exist for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++
++ for auth in os.listdir(auth_dir):
++ p = os.open(auth_dir + "/" + auth, 0)
++ value = os.read(p, 256)
++ print auth + ": " + value.rstrip()
++ os.close(p)
++
++ return
++
++def lio_target_show_chap_discovery_auth(option, opt_str, value, parser):
++
++ auth_dir = lio_root + "/discovery_auth"
++ if not os.path.isdir(auth_dir):
++ lio_err("iSCSI Discovery Authentication directory " + auth_dir + " does not exist")
++
++ for auth in os.listdir(auth_dir):
++ p = os.open(auth_dir + "/" + auth, 0)
++ value = os.read(p, 256)
++ print auth + ": " + value.rstrip()
++ os.close(p)
++
++ return
++
++def lio_target_show_node_tcq(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2]);
++ initiator_iqn = initiator_iqn.lower();
++
++ nacl = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn
++ if not os.path.isdir(nacl):
++ lio_err("iSCSI Initiator ACL: " + initiator_iqn + " does not exist for iSCSI Target Portal Group: " + iqn + " " + tpgt)
++
++ tcq_depth_file = nacl + "/cmdsn_depth"
++ p = os.open(tcq_depth_file, 0)
++ value = os.read(p, 8)
++ print value.rstrip()
++ os.close(p)
++
++ return
++
++def lio_target_alua_show_tgptgp(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ lun = str(value[2]);
++
++ lun_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ if not os.path.isdir(lun_dir):
++ lio_err("LIO-Target Port/LUN: " + lun + " does not exist on: " + iqn + " " + tpgt)
++
++ show_tp_pt_gp_op = "cat " + lun_dir + "/alua_tg_pt_gp"
++ ret = os.system(show_tp_pt_gp_op)
++ if ret:
++ lio_err("Unable to show ALUA Target Port Group: " + tg_pt_gp_name + " for LUN: " + lun + " on " + iqn + " " + tpgt)
++
++ return
++
++def lio_target_list_endpoints(option, opt_str, value, parser):
++
++ iqn_root = os.listdir(lio_root)
++
++ for iqn in iqn_root:
++ if iqn == "lio_version":
++ continue
++ if iqn == "discovery_auth":
++ continue
++
++ print "\------> " + iqn
++
++ tpg_root = lio_root + "/" + iqn
++ for tpg in os.listdir(tpg_root):
++ if tpg == "fabric_statistics":
++ continue
++
++ p = os.open(tpg_root + "/" + tpg + "/param/TargetAlias", 0)
++ value = os.read(p, 256)
++ print " \-------> " + tpg + " TargetAlias: " + value.rstrip()
++ os.close(p)
++
++ print " TPG Status:",
++ p = os.open(tpg_root + "/" + tpg + "/enable", 0)
++ value = os.read(p, 8)
++ enable_bit = value.rstrip();
++ if enable_bit == '1':
++ print "ENABLED"
++ else:
++ print "DISABLED"
++ os.close(p)
++
++ print " TPG Network Portals:"
++ np_root = tpg_root + "/" + tpg + "/np"
++ for np in os.listdir(np_root):
++ print " \-------> " + np
++
++ print " TPG Logical Units:"
++ lun_root = tpg_root + "/" + tpg + "/lun"
++ for lun in os.listdir(lun_root):
++ port_dir = lun_root + "/" + lun
++ for port in os.listdir(port_dir):
++ if port == "alua_tg_pt_gp":
++ continue
++ if port == "alua_tg_pt_offline":
++ continue
++ if port == "alua_tg_pt_status":
++ continue
++ if port == "alua_tg_pt_write_md":
++ continue
++ if port == "statistics":
++ continue
++
++ port_link = port_dir + "/" + port
++ if not os.path.islink(port_link):
++ continue
++
++ sourcelink = os.readlink(port_link)
++ # Skip over ../../../../../ in sourcelink"
++ print " \-------> " + lun + "/" + port + " -> " + sourcelink[18:]
++
++
++ return
++
++def lio_target_list_lunacls(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ iqn_root = os.listdir(lio_root)
++
++ nacl_root_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls"
++ nacl_root = os.listdir(nacl_root_dir)
++ for nacl in nacl_root:
++ print "\------> InitiatorName ACL: " + nacl
++ print " Logical Unit ACLs: "
++ lun_root_dir = nacl_root_dir + "/" + nacl
++ lun_root = os.listdir(lun_root_dir)
++ for lun in lun_root:
++ ret = re.search('lun_', lun)
++ if not ret:
++ continue
++
++ wp_attrib = lun_root_dir + "/" + lun + "/write_protect"
++ wp_file = open(wp_attrib);
++ line = wp_file.readline()
++ wp_bit = line.rstrip()
++ if wp_bit == '1':
++ wp_info = "ENABLED"
++ else:
++ wp_info = "DISABLED"
++
++ lun_link_dir = lun_root_dir + "/" + lun
++ for lun_link in os.listdir(lun_link_dir):
++ if lun_link == "write_protect":
++ continue
++ if lun_link == "statistics":
++ continue
++
++ if not os.path.islink(lun_link_dir + "/" + lun_link):
++ continue
++
++ sourcelink = os.readlink(lun_link_dir + "/" + lun_link)
++ # Skip over ../../../../../../ in sourcelink"
++ print " \-------> " + lun + " -> " + sourcelink[21:]
++ print " \-------> Write Protect for " + lun + ": " + wp_info
++
++ return
++
++def lio_target_list_nodeacls(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ iqn_root = os.listdir(lio_root)
++
++ nacl_root_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls"
++ nacl_root = os.listdir(nacl_root_dir)
++ for nacl in nacl_root:
++ print "\------> InitiatorName: " + nacl
++ info_attrib = nacl_root_dir + "/" + nacl + "/info"
++ file = open(info_attrib, "r")
++ line = file.readline()
++ ret = re.search('No active iSCSI Session for Initiator Endpoint', line)
++ if ret:
++ print " No active iSCSI Session for Initiator Endpoint"
++ else:
++ line = file.readline()
++ while line:
++ print " " + line.rstrip()
++ line = file.readline()
++
++ file.close()
++
++ return
++
++def lio_target_list_nps(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ np_root = os.listdir(lio_root + "/" + iqn + "/tpgt_" + tpgt + "/np")
++ for np in np_root:
++ print np
++
++ return
++
++def lio_target_list_targetnames(option, opt_str, value, parser):
++
++ iqn_root = os.listdir(lio_root)
++
++ # Loop through LIO-Target IQN list
++ for iqn in iqn_root:
++ if iqn == "lio_version":
++ continue
++ if iqn == "discovery_auth":
++ continue
++
++ print iqn
++
++ return
++
++def lio_target_list_node_attr(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2])
++
++ attr_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/attrib/"
++ if os.path.isdir(attr_dir) == False:
++ lio_err("Unable to locate node attr_dir: " + attr_dir)
++
++ for attr in os.listdir(attr_dir):
++ p = open(attr_dir + "/" + attr, 'rU')
++ if not p:
++ lio_err("Unable to open attr: " + attr_dir + "/" + attr)
++
++ val = p.read()
++ p.close()
++
++ print attr + "=" + val.rstrip()
++
++ return
++
++def lio_target_set_node_attr(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2])
++ attr = str(value[3])
++ val = str(value[4])
++
++ attr_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/attrib/"
++ if os.path.isdir(attr_dir) == False:
++ lio_err("Unable to locate node attr_dir: " + attr_dir)
++
++ p = open(attr_dir + "/" + attr, 'w')
++ if not p:
++ lio_err("Unable to open node attr: " + attr_dir + "/" + attr)
++
++ ret = p.write(val)
++ if ret:
++ lio_err("Unable to set node attr: " + attr_dir + "/" + attr)
++
++ p.close()
++ print "Successfully set Initiator Node attribute: " + attr + " to: " + val
++
++ return
++
++def lio_target_list_node_param(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ initiator_iqn = str(value[2])
++
++ param_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/" + initiator_iqn + "/param"
++ if os.path.isdir(param_dir) == False:
++ lio_err("Unable to locate node param_dir: " + param_dir)
++
++ for param in os.listdir(param_dir):
++ p = open(param_dir + "/" + param, 'rU')
++ if not p:
++ lio_err("Unable to open attr: " + param_dir + "/" + param)
++
++ val = p.read()
++ p.close()
++
++ print param + "=" + val.rstrip()
++
++ return
++
++
++def lio_target_list_tpg_attr(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ attr_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/attrib"
++ if os.path.isdir(attr_dir) == False:
++ lio_err("Unable to locate tpg attr_dir: " + attr_dir)
++
++ for attr in os.listdir(attr_dir):
++ p = open(attr_dir + "/" + attr, 'rU')
++ if not p:
++ lio_err("Unable to open attr: " + attr_dir + "/" + attr)
++
++ val = p.read()
++ p.close()
++
++ print attr + "=" + val.rstrip()
++
++ return
++
++def lio_target_set_tpg_attr(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ attr = str(value[2]);
++ val = str(value[3]);
++
++ attr_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/attrib"
++ if os.path.isdir(attr_dir) == False:
++ lio_err("Unable to locate tpg attr_dir: " + attr_dir)
++
++ p = open(attr_dir + "/" + attr, 'w')
++ if not p:
++ lio_err("Unable to open tpg attr: " + attr_dir + "/" + attr)
++
++ ret = p.write(val)
++ if ret:
++ lio_err("Unable to set tpg attr: " + attr_dir + "/" + attr)
++
++ p.close()
++ print "Successfully set TPG attribute: " + attr + " to: " + val
++
++ return
++
++def lio_target_list_tpg_param(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++
++ param_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/param"
++ if os.path.isdir(param_dir) == False:
++ lio_err("Unable to locate tpg param dir: " + param_dir)
++
++ for param in os.listdir(param_dir):
++ p = open(param_dir + "/" + param, 'rU')
++ if not p:
++ lio_err("Unable to open param: " + param_dir + "/" + param)
++
++ val = p.read()
++ p.close()
++
++ print param + "=" + val.rstrip()
++
++ return
++
++def lio_target_set_tpg_param(option, opt_str, value, parser):
++ iqn = str(value[0]);
++ iqn = iqn.lower();
++ tpgt = str(value[1]);
++ param = str(value[2]);
++ val = str(value[3]);
++
++ param_dir = lio_root + "/" + iqn + "/tpgt_" + tpgt + "/param"
++ if os.path.isdir(param_dir) == False:
++ lio_err("Unable to locate tpg param dir: " + param_dir)
++
++ p = open(param_dir + "/" + param, 'w')
++ if not p:
++ lio_err("Unable to open tpg attr: " + param_dir + "/" + param)
++
++ val = val + " "
++ ret = p.write(val)
++ if ret:
++ lio_err("Unable to write tpg attr: " + param_dir + "/" + param)
++
++ p.close()
++ print "Successfully set TPG parameter: " + param + " to: " + val
++
++ return
++
++def lio_target_unload(option, opt_str, value, parser):
++
++ if not os.path.isdir(lio_root):
++ lio_err("Unable to access lio_root: " + lio_root)
++
++ iqn_root = os.listdir(lio_root)
++
++ # Loop through LIO-Target IQN list
++ for iqn in iqn_root:
++ if iqn == "lio_version":
++ continue
++ if iqn == "discovery_auth":
++ continue
++
++ # Loop through LIO-Target IQN+TPGT list
++ tpg_root = os.listdir(lio_root + "/" + iqn);
++ for tpgt_tmp in tpg_root:
++ if tpgt_tmp == "fabric_statistics":
++ continue
++
++ tpgt_tmp2 = tpgt_tmp.split('_')
++ tpgt = tpgt_tmp2[1]
++
++ tpg_val = [iqn,tpgt]
++ __lio_target_del_tpg(None, None, tpg_val, None, 0)
++
++ __lio_target_del_iqn(None, None, iqn, None, 0)
++
++ rmdir_op = "rmdir " + lio_root
++ ret = os.system(rmdir_op)
++ if ret:
++ print "Unable to release lio_root: " + lio_root
++
++ rmmod_op = "rmmod iscsi_target_mod"
++ ret = os.system(rmmod_op)
++ if ret:
++ print "Unable to unload iscsi_target_mod"
++
++ return
++
++def lio_target_version(option, opt_str, value, parser):
++
++ os.system("cat /sys/kernel/config/target/iscsi/lio_version")
++ return
++
++def main():
++
++ parser = OptionParser()
++ parser.add_option("--addlunacl", action="callback", callback=lio_target_add_lunacl, nargs=5,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN TPG_LUN MAPPED_LUN", help="Add iSCSI Initiator LUN ACL to LIO-Target Portal Group LUN")
++ parser.add_option("--addnodeacl", action="callback", callback=lio_target_add_nodeacl, nargs=3,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN", help="Add iSCSI Initiator ACL to LIO-Target Portal Group")
++ parser.add_option("--addnp", action="callback", callback=lio_target_add_np, nargs=3,
++ type="string", dest="TARGET_IQN TPGT IP:PORT", help="Add LIO-Target IPv6 or IPv4 network portal")
++ parser.add_option("--addlun", action="callback", callback=lio_target_add_port, nargs=5,
++ type="string", dest="TARGET_IQN TPGT LUN PORT_ALIAS TCM_HBA/DEV ", help="Create LIO-Target Logical Unit")
++ parser.add_option("--addtpg", action="callback", callback=lio_target_add_tpg, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="Create LIO-Target portal group")
++ parser.add_option("--aluasecmd", action="callback", callback=lio_alua_process_secondary_md, nargs=3,
++ type="string", dest="TARGET_IQN TPGT LUN", help="Process ALUA secondary metadata for Port/LUN");
++ parser.add_option("--cleartgptoff","--clearaluaoff", action="callback", callback=lio_target_alua_clear_tgpt_offline, nargs=3,
++ type="string", dest="TARGET_IQN TPGT LUN", help="Clear ALUA Target Port Secondary State OFFLINE")
++ parser.add_option("--dellunacl", action="callback", callback=lio_target_del_lunacl, nargs=4,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN MAPPED_LUN", help="Delete iSCSI Initiator LUN ACL from LIO-Target Portal Group LUN")
++ parser.add_option("--delnodeacl", action="callback", callback=lio_target_del_nodeacl, nargs=3,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN", help="Delete iSCSI Initiator ACL from LIO-Target Portal Group")
++ parser.add_option("--delnp", action="callback", callback=lio_target_del_np, nargs=3,
++ type="string", dest="TARGET_IQN TPGT IP:PORT", help="Delete LIO-Target IPv6 or IPv4 network portal")
++ parser.add_option("--deliqn", action="callback", callback=lio_target_del_iqn, nargs=1,
++ type="string", dest="TARGET_IQN", help="Delete LIO-Target IQN Endpoint")
++ parser.add_option("--dellun", action="callback", callback=lio_target_del_port, nargs=3,
++ type="string", dest="TARGET_IQN TPGT LUN", help="Delete LIO-Target Logical Unit")
++ parser.add_option("--deltpg", action="callback", callback=lio_target_del_tpg, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="Delete LIO-Target Portal Group")
++ parser.add_option("--demomode", "--permissive", action="callback", callback=lio_target_tpg_demomode, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="Disable all iSCSI Initiator ACL requirements (enable DemoMode) for LIO-Target Portal Group (Disabled by default)")
++ parser.add_option("--disableauth", action="callback", callback=lio_target_tpg_disableauth, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="Disable iSCSI Authentication for LIO-Target Portal Group (Enabled by default)")
++ parser.add_option("--disablelunwp", action="callback", callback=lio_target_disable_lunwp, nargs=4,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN MAPPED_LUN", help="Clear Write Protect bit for iSCSI Initiator LUN ACL")
++ parser.add_option("--disabletpg", action="callback", callback=lio_target_disable_tpg, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="Disable LIO-Target Portal Group")
++ parser.add_option("--enableaclmode", action="callback", callback=lio_target_enableaclmode, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="Enable iSCSI Initiator ACL requirement mode for LIO-Target Portal Group (Enabled by default)")
++ parser.add_option("--enableauth", action="callback", callback=lio_target_enable_auth, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="Enable iSCSI Authentication for LIO-Target Portal Group (Enabled by default)")
++ parser.add_option("--enablelunwp", action="callback", callback=lio_target_enable_lunwp, nargs=4,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN MAPPED_LUN", help="Set Write Protect bit for iSCSI Initiator LUN ACL")
++ parser.add_option("--enabletpg", action="callback", callback=lio_target_enable_tpg, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="Enable LIO-Target Portal Group")
++ parser.add_option("--listendpoints", action="callback", callback=lio_target_list_endpoints, nargs=0,
++ help="List iSCSI Target Endpoints")
++ parser.add_option("--listlunacls", action="callback", callback=lio_target_list_lunacls, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="List iSCSI Initiator LUN ACLs for LIO-Target Portal Group")
++ parser.add_option("--listnodeacls", action="callback", callback=lio_target_list_nodeacls, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="List iSCSI Initiator ACLs for LIO-Target Portal Group")
++ parser.add_option("--listnodeattr", action="callback", callback=lio_target_list_node_attr, nargs=3,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN", help="List iSCSI Initiator ACL attributes for LIO-Target Portal Group")
++ parser.add_option("--listnodeparam", action="callback", callback=lio_target_list_node_param, nargs=3,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN", help="List iSCSI Initiator ACL RFC-3720 parameters for LIO-Target Portal Group")
++ parser.add_option("--listnps", action="callback", callback=lio_target_list_nps, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="List LIO-Target Portal Group Network Portals")
++ parser.add_option("--listtargetnames", action="callback", callback=lio_target_list_targetnames, nargs=0,
++ help="List iSCSI Target Names")
++ parser.add_option("--listtpgattr", action="callback", callback=lio_target_list_tpg_attr, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="List LIO-Target Portal Group attributes")
++ parser.add_option("--listtpgparam", action="callback", callback=lio_target_list_tpg_param, nargs=2,
++ type="string", dest="TARGET_IQN TPGT", help="List LIO-Target Portal Group RFC-3720 parameters")
++ parser.add_option("--setchapauth", action="callback", callback=lio_target_set_chap_auth, nargs=5,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN USER PASS", help="Set CHAP authentication information for iSCSI Initiator Node ACL");
++ parser.add_option("--setchapmutualauth", action="callback", callback=lio_target_set_chap_mutual_auth, nargs=5,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN USER_IN PASS_IN", help="Set CHAP mutual authentication information for iSCSI Initiator Node ACL");
++ parser.add_option("--setchapdiscenforce", action="callback", callback=lio_target_set_enforce_discovery_auth, nargs=1,
++ type="string", dest="Enforce=1, NoEnforcement=0", help="Set CHAP authentication enforcement for iSCSI Discovery Sessions");
++ parser.add_option("--setchapdiscauth", action="callback", callback=lio_target_set_chap_discovery_auth, nargs=2,
++ type="string", dest="USER PASS", help="Set CHAP authentication information for iSCSI Discovery Authentication")
++ parser.add_option("--setchapdiscmutualauth", action="callback", callback=lio_target_set_chap_mutual_discovery_auth, nargs=2,
++ type="string", dest="USER PASS", help="Set CHAP mutual authentication information for iSCSI Discovery Authentication")
++ parser.add_option("--setnodeattr", action="callback", callback=lio_target_set_node_attr, nargs=5,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN <ATTRIBUTE> <VALUE>", help="Set iSCSI Initiator ACL Attribute")
++ parser.add_option("--setnodetcq", action="callback", callback=lio_target_set_node_tcq, nargs=4,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN DEPTH", help="Set iSCSI Initiator ACL TCQ Depth for LIO-Target Portal Group")
++ parser.add_option("--settpgattr", action="callback", callback=lio_target_set_tpg_attr, nargs=4,
++ type="string", dest="TARGET_IQN TPGT <ATTRIB> <VALUE>", help="Set LIO-Target Port Group Attribute")
++ parser.add_option("--settpgparam", action="callback", callback=lio_target_set_tpg_param, nargs=4,
++ type="string", dest="TARGET_IQN TPGT <PARAMETER> <VALUE>", help="Set LIO-Target Port Group RFC-3720 parameter")
++ parser.add_option("--settgptgp","--setaluatpg", action="callback", callback=lio_target_alua_set_tgptgp, nargs=4,
++ type="string", dest="TARGET_IQN TPGT LUN TG_PT_GP_NAME", help="Set ALUA Target Port Group for LIO-Target Port/LUN")
++ parser.add_option("--settgptoff","--setaluaoff", action="callback", callback=lio_target_alua_set_tgpt_offline, nargs=3,
++ type="string", dest="TARGET_IQN TPGT LUN", help="Set ALUA Target Port Secondary State OFFLINE")
++ parser.add_option("--showchapauth", action="callback", callback=lio_target_show_chap_auth, nargs=3,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN", help="Show CHAP authentication information for iSCSI Initiator Node ACL");
++ parser.add_option("--showchapdiscauth", action="callback", callback=lio_target_show_chap_discovery_auth, nargs=0,
++ help="Show CHAP authentication information for iSCSI Discovery portal");
++ parser.add_option("--shownodetcq", action="callback", callback=lio_target_show_node_tcq, nargs=3,
++ type="string", dest="TARGET_IQN TPGT INITIATOR_IQN", help="Show iSCSI Initiator ACL TCQ Depth for LIO-Target Portal Group")
++ parser.add_option("--showtgptgp", action="callback", callback=lio_target_alua_show_tgptgp, nargs=3,
++ type="string", dest="TARGET_IQN TPGT LUN", help="Show ALUA Target Port Group for LIO-Target Port/LUN")
++ parser.add_option("--unload", action="callback", callback=lio_target_unload, nargs=0,
++ help="Unload LIO-Target")
++ parser.add_option("--version", action="callback", callback=lio_target_version, nargs=0,
++ help="Display LIO-Target version information")
++
++ (options, args) = parser.parse_args()
++ if len(sys.argv) == 1:
++ parser.print_help()
++ sys.exit(0)
++ elif not re.search('--', sys.argv[1]):
++ lio_err("Unknown CLI option: " + sys.argv[1])
++
++if __name__ == "__main__":
++ main()
+diff --git a/targetcli/tcm_dump.py b/targetcli/tcm_dump.py
+new file mode 100644
+index 0000000..bf80632
+--- /dev/null
++++ b/targetcli/tcm_dump.py
+@@ -0,0 +1,366 @@
++import os, sys, shutil
++import subprocess as sub
++from subprocess import Popen, PIPE
++import string
++import re
++import datetime, time
++from optparse import OptionParser
++
++import tcm_node
++import tcm_pscsi
++import tcm_iblock
++import tcm_ramdisk
++import tcm_fileio
++
++import lio_dump
++import tcm_fabric
++
++tcm_root = "/sys/kernel/config/target/core"
++
++def tcm_dump_hba_devices():
++ pass
++
++def tcm_dump_configfs(option, opt_str, value, parser):
++
++ if not os.path.isdir(tcm_root):
++ print "Unable to access tcm_root: " + tcm_root
++ sys.exit(1)
++
++ print "modprobe target_core_mod"
++
++ # Loop through ALUA Logical Unit Groups
++ # Note that the 'default_lu_gp' is automatically created when
++ # target_core_mod is loaded.
++ print "#### ALUA Logical Unit Groups"
++ for lu_gp in os.listdir(tcm_root + "/alua/lu_gps"):
++ if lu_gp == "default_lu_gp":
++ continue
++
++ print "mkdir -p " + tcm_root + "/alua/lu_gps/" + lu_gp
++ lu_gp_id_file = tcm_root + "/alua/lu_gps/" + lu_gp + "/lu_gp_id"
++ p = os.open(lu_gp_id_file, 0)
++ value = os.read(p, 8)
++ os.close(p)
++ if not value:
++ continue
++ print "echo " + value.rstrip() + " > " + lu_gp_id_file
++
++ # Loop through HBA list
++ for f in os.listdir(tcm_root):
++ if f == "alua":
++ continue;
++
++# print "mkdir -p " + tcm_root + "/" + f
++
++ dev_root = tcm_root + "/" + f + "/"
++ for g in os.listdir(dev_root):
++ if g == "hba_info" or g == "hba_mode":
++ continue;
++
++ # Dump device aka storage object
++ print "#### Parameters for TCM subsystem plugin storage object reference"
++
++ # Generate subsystem dependent configfs ops for association to
++ # an target_core_mod storage object.
++ result = re.search('pscsi_', f)
++ if result:
++ dev = dev_root + g
++ params = tcm_pscsi.pscsi_get_params(dev)
++ if not params:
++ continue
++ print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ result = re.search('iblock_', f)
++ if result:
++ dev = dev_root + g
++ params = tcm_iblock.iblock_get_params(dev)
++ if not params:
++ continue
++ print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ result = re.search('rd_dr_', f)
++ if result:
++ dev = dev_root + g
++ params = tcm_ramdisk.rd_get_params(dev)
++ if not params:
++ continue
++ print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ result = re.search('rd_mcp_', f)
++ if result:
++ dev = dev_root + g
++ params = tcm_ramdisk.rd_get_params(dev)
++ if not params:
++ continue
++ print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ result = re.search('fileio_', f)
++ if result:
++ dev = dev_root + g
++ params = tcm_fileio.fd_get_params(dev)
++ if not params:
++ continue
++ print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++
++ # Dump T10 VP Unit Serial for all non Target_Core_Mod/pSCSI objects
++ result = re.search('pscsi_', f)
++ if not result:
++ unit_serial_file = dev_root + g + "/wwn/vpd_unit_serial"
++ p = os.open(unit_serial_file, 0)
++ value = os.read(p, 512)
++ off = value.index('Number: ')
++ off += 8 # Skip over "Number: "
++ unit_serial = value[off:]
++ # Note that this will handle read, parse and set any PR APTPL metadata
++ print "tcm_node --setunitserialwithmd " + f + "/" + g + " " + unit_serial.rstrip()
++ os.close(p)
++
++ # Dump device object alias
++ alias_file = dev_root + g + "/alias"
++ p = open(alias_file, 'r')
++ value = p.read(512)
++ value = value.rstrip()
++ p.close()
++ if value:
++ print "echo -n \"" + value + "\" > " + dev_root + g + "/alias"
++
++ # Dump ALUA Logical Unit Group
++ lu_gp_file = dev_root + g + "/alua_lu_gp"
++ p = os.open(lu_gp_file, 0)
++ value = os.read(p, 512)
++ os.close(p)
++ if value:
++ lu_gp_tmp = value.split('\n')
++ lu_gp_out = lu_gp_tmp[0]
++ off = lu_gp_out.index('Alias: ')
++ off += 7 # Skip over "Alias: "
++ lu_gp_name = lu_gp_out[off:]
++ # Only need to dump if storage object is NOT part of
++ # the 'default_lu_gp'
++ if not re.search(lu_gp_name, 'default_lu_gp'):
++ print "echo " + lu_gp_name + " > " + lu_gp_file
++
++ # Loop through ALUA Target Port Groups
++ if os.path.isdir(dev_root + g + "/alua/") == True:
++ print "#### ALUA Target Port Groups"
++ for tg_pt_gp in os.listdir(dev_root + g + "/alua/"):
++ tg_pt_gp_id_file = dev_root + g + "/alua/" + tg_pt_gp + "/tg_pt_gp_id"
++ p = os.open(tg_pt_gp_id_file, 0)
++ value = os.read(p, 8)
++ os.close(p)
++ if not value:
++ continue
++ print "tcm_node --addaluatpgwithmd " + f + "/" + g + " " + tg_pt_gp + " " + value.rstrip()
++ # Dump the ALUA types
++ tg_pt_gp_type_file = dev_root + g + "/alua/" + tg_pt_gp + "/alua_access_type"
++ p = os.open(tg_pt_gp_type_file, 0)
++ value = os.read(p, 32)
++ os.close(p)
++ if value:
++ value = value.rstrip()
++ alua_type = 0
++
++ if re.search('Implict and Explict', value):
++ alua_type = 3
++ elif re.search('Explict', value):
++ alua_type = 2
++ elif re.search('Implict', value):
++ alua_type = 1
++
++ print "echo " + str(alua_type) + " > " + tg_pt_gp_type_file
++
++ # Dump the preferred bit
++ tg_pt_gp_pref_file = dev_root + g + "/alua/" + tg_pt_gp + "/preferred"
++ p = os.open(tg_pt_gp_pref_file, 0)
++ value = os.read(p, 8)
++ os.close(p)
++ if value:
++ print "echo " + value.rstrip() + " > " + tg_pt_gp_pref_file
++ # Dump the Active/NonOptimized Delay
++ tg_pt_gp_nonop_delay_file = dev_root + g + "/alua/" + tg_pt_gp + "/nonop_delay_msecs"
++ p = os.open(tg_pt_gp_nonop_delay_file, 0)
++ value = os.read(p, 8)
++ os.close(p)
++ if value:
++ print "echo " + value.rstrip() + " > " + tg_pt_gp_nonop_delay_file
++ # Dump the Transition Delay
++ tg_pt_gp_trans_delay_file = dev_root + g + "/alua/" + tg_pt_gp + "/trans_delay_msecs"
++ p = os.open(tg_pt_gp_trans_delay_file, 0)
++ value = os.read(p, 8)
++ os.close(p)
++ if value:
++ print "echo " + value.rstrip() + " > " + tg_pt_gp_trans_delay_file
++
++ # Dump device attributes
++ print "#### Attributes for " + dev_root + g
++ dev_attrib_root = dev_root + g + "/attrib/"
++ for h in os.listdir(dev_attrib_root):
++ # The hw_* prefixed attributes are RO
++ if h == "hw_queue_depth":
++ continue
++ if h == "hw_max_sectors":
++ continue
++ if h == "hw_block_size":
++ continue
++ # Do not change block-size for target_core_mod/pSCSI
++ if h == "block_size":
++ result = re.search('pscsi_', f)
++ if result:
++ continue
++
++ attrib_file = dev_attrib_root + h
++ p = os.open(attrib_file, 0)
++ value = os.read(p, 8)
++ print "echo " + value.rstrip() + " > " + attrib_file
++ os.close(p)
++
++ # Dump snapshot attributes
++ snap_attrib_root = dev_root + g + "/snap/"
++ if (os.path.isdir(snap_attrib_root) == False):
++ continue
++
++ snap_enabled = 0
++ enabled_attr_file = snap_attrib_root + "enabled"
++ p = open(enabled_attr_file, 'rU')
++ if not p:
++ continue
++ value = p.read()
++ enabled = value.rstrip()
++ p.close()
++ if enabled != "1":
++ continue
++
++ snap_enabled = 1
++ print "#### Snapshot Attributes for " + dev_root + g
++ for s in os.listdir(snap_attrib_root):
++ if s == "pid":
++ continue
++ if s == "usage":
++ continue
++ if s == "enabled":
++ continue
++
++ attrib_file = snap_attrib_root + s
++ p = open(attrib_file, 'rU')
++ value = p.read()
++ p.close()
++ attr_val = value.rstrip()
++ print "echo " + attr_val + " > " + attrib_file
++
++ if snap_enabled == 1:
++ print "tcm_node --lvsnapstart " + f + "/" + g
++
++def tcm_backup_to_file(option, opt_str, value, parser):
++ datetime = str(value)
++
++ if not os.path.isdir(tcm_root):
++ print "Unable to access tcm_root: " + tcm_root
++ sys.exit(1)
++
++ backup_dir = "/etc/target/backup"
++ if not os.path.isdir(backup_dir):
++ op = "mkdir " + backup_dir
++ ret = os.system(op)
++ if ret:
++ print "Unable to open backup_dir"
++ sys.exit(1)
++
++ op = "tcm_dump --stdout"
++ p = sub.Popen(op, shell=True, stdout=sub.PIPE).stdout
++ if not p:
++ print "Unable to dump Target_Core_Mod/ConfigFS running state"
++ sys.exit(0)
++
++ print "Making backup of Target_Core_Mod/ConfigFS with timestamp: " + datetime
++ backup_file = backup_dir + "/tcm_backup-" + datetime + ".sh"
++ if os.path.isfile(backup_file):
++ print "Target_Core_Mod backup_file: " + backup_file + "already exists, exiting"
++ p.close()
++ sys.exit(1)
++
++ back = open(backup_file, 'w')
++
++ line = p.readline()
++ while line:
++ print >>back, line.rstrip()
++ line = p.readline()
++
++ p.close()
++ back.close()
++ return backup_file
++
++def tcm_full_backup(option, opt_str, value, parser):
++ overwrite = str(value)
++
++ now = datetime.datetime.now()
++ tmp = str(now)
++ tmp2 = tmp.split(' ')
++ timestamp = tmp2[0] + "_" + tmp2[1]
++
++ tcm_file = "/etc/target/tcm_start.sh"
++ lio_file = "/etc/target/lio_start.sh"
++ lio_active = 0
++
++ ret = tcm_fabric.fabric_backup_to_file_all(timestamp)
++ if ret:
++ print "Unable to backup tcm_fabric.modules"
++
++ if os.path.isdir("/sys/kernel/config/target/iscsi"):
++ lio_file_new = lio_dump.lio_backup_to_file(None, None, timestamp, None)
++ if not lio_file_new:
++ sys.exit(1)
++ lio_active = 1
++ print "Generated LIO-Target config: " + lio_file_new
++
++
++ tcm_file_new = tcm_backup_to_file(None, None, timestamp, None)
++ if not tcm_file_new:
++ sys.exit(1)
++
++ print "Generated Target_Core_Mod config: " + tcm_file_new
++
++ if overwrite != "1":
++ print "Not updating default config"
++ return
++
++ if lio_active:
++ ret = shutil.copyfile(lio_file_new, lio_file)
++ if ret:
++ print "Unable to copy " + lio_file_new
++ sys.exit(1)
++ print "Successfully updated default config " + lio_file
++
++ ret = shutil.copyfile(tcm_file_new, tcm_file)
++ if ret:
++ print "Unable to copy " + tcm_file_new
++ sys.exit(1)
++
++ print "Successfully updated default config " + tcm_file
++
++def tcm_overwrite_default(option, opt_str, value, parser):
++
++ input = raw_input("Are you sure you want to overwrite the default configuration? Type 'yes': ")
++ if input != "yes":
++ sys.exit(0)
++
++ val = "1"
++ tcm_full_backup(None, None, val, None)
++
++def main():
++
++ parser = OptionParser()
++ parser.add_option("--b","--backup", action="callback", callback=tcm_full_backup, nargs=1,
++ type="string", dest="OVERWRITE", help="Do backup of TCM and storage fabric modules, and optionally overwrite default config data")
++ parser.add_option("--o","--overwrite", action="callback", callback=tcm_overwrite_default, nargs=0,
++ help="Overwrite default config data of TCM and storage fabric modules")
++ parser.add_option("--s","--stdout", action="callback", callback=tcm_dump_configfs, nargs=0,
++ help="Dump running Target_Core_Mod/ConfigFS syntax to STDOUT")
++ parser.add_option("--t", "--tofile", action="callback", callback=tcm_backup_to_file, nargs=1,
++ type="string", dest="DATE_TIME", help="Backup running Target_Core_Mod/ConfigFS syntax to /etc/target/backup/tcm_backup-<DATE_TIME>.sh")
++
++ (options, args) = parser.parse_args()
++ if len(sys.argv) == 1:
++ parser.print_help()
++ sys.exit(0)
++ elif not re.search('--', sys.argv[1]):
++ print "Unknown CLI option: " + sys.argv[1]
++ sys.exit(1)
++
++if __name__ == "__main__":
++ main()
+diff --git a/targetcli/tcm_fabric.py b/targetcli/tcm_fabric.py
+new file mode 100644
+index 0000000..8a843d4
+--- /dev/null
++++ b/targetcli/tcm_fabric.py
+@@ -0,0 +1,532 @@
++import os, sys, shutil
++import subprocess as sub
++import string
++import re
++import datetime, time
++import optparse
++
++target_root = "/sys/kernel/config/target/"
++spec_root = "/var/target/fabric/"
++
++def fabric_configfs_dump(fabric_name, fabric_root, module_name):
++
++ if not os.path.isdir(fabric_root):
++ print "Unable to access fabric_root: " + fabric_root
++ sys.exit(1)
++
++ iqn_root = os.listdir(fabric_root)
++
++ # This will load up the fabric module
++ print "modprobe " + module_name
++ print "mkdir " + fabric_root
++
++# print "#### " + fabric_name + " Discovery authentication information"
++ auth_dir = fabric_root + "/discovery_auth"
++ if os.path.isdir(auth_dir) == True:
++ for auth in os.listdir(auth_dir):
++ if auth == "authenticate_target":
++ continue
++
++ auth_file = auth_dir + "/" + auth
++ p = os.open(auth_file, 0)
++ value = os.read(p, 256)
++ ret = value.isspace()
++ if ret:
++ os.close(p)
++ continue
++ print "echo -n " + value.rstrip() + " > " + auth_file
++ os.close(p)
++
++ iqn_root = os.listdir(fabric_root)
++
++ # Loop through LIO-Target IQN list
++ for iqn in iqn_root:
++ if not os.path.isdir(fabric_root + "/" + iqn):
++ continue
++ if iqn == "lio_version":
++ continue
++ if iqn == "discovery_auth":
++ continue
++
++ # Loop through LIO-Target IQN+TPGT list
++ tpg_root = os.listdir(fabric_root + "/" + iqn);
++ for tpgt_tmp in tpg_root:
++ if tpgt_tmp == "fabric_statistics":
++ continue
++
++ tpgt_tmp2 = tpgt_tmp.split('_')
++ tpgt = tpgt_tmp2[1]
++
++# print "#### Network portals for iSCSI Target Portal Group"
++# np_root = os.listdir(fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/np")
++# for np in np_root:
++# print "mkdir -p " + fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/np/" + np
++
++
++ # Dump Nexus attribute (when available)
++ nexus_file = fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/nexus"
++ if os.path.isfile(nexus_file):
++ print "mkdir -p " + fabric_root + "/" + iqn + "/tpgt_" + tpgt
++ p = os.open(nexus_file, 0)
++ value = os.read(p, 256)
++ print "echo " + value.rstrip() + " > " + nexus_file
++ os.close(p)
++
++ print "#### " + fabric_name + " Target Ports"
++ lun_root = os.listdir(fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/lun")
++ for lun_tmp in lun_root:
++ lun_tmp2 = lun_tmp.split('_')
++ lun = lun_tmp2[1]
++
++ lun_dir = fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun
++ print "mkdir -p " + lun_dir
++
++ port_root = os.listdir(lun_dir)
++ for port in port_root:
++ if port == "alua_tg_pt_gp":
++ continue
++ if port == "alua_tg_pt_offline":
++ continue
++ if port == "alua_tg_pt_status":
++ continue
++ if port == "alua_tg_pt_write_md":
++ continue
++
++ if not os.path.islink(lun_dir + "/" + port):
++ continue
++
++ port_link = fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/lun/lun_" + lun + "/" + port
++ sourcelink = os.readlink(port_link)
++ sourcelink2 = os.path.join(os.path.dirname(port_link), sourcelink)
++ print "ln -s " + sourcelink2 + " " + port_link
++
++ # Dump ALUA Target Port Group
++ tg_pt_gp_file = lun_dir + "/alua_tg_pt_gp"
++ p = os.open(tg_pt_gp_file, 0)
++ try:
++ value = os.read(p, 512)
++ except:
++ os.close(p)
++ continue
++ os.close(p)
++ if value:
++ tg_pt_gp_tmp = value.split('\n')
++ tg_pt_gp_out = tg_pt_gp_tmp[0]
++ off = tg_pt_gp_out.index('Alias: ')
++ off += 7 # Skip over "Alias: "
++ tg_pt_gp_name = tg_pt_gp_out[off:]
++ # Only need to dump if LIO-Target Port is NOT partof
++ # the 'default_tg_pt_gp'
++ if not re.search(tg_pt_gp_name, 'default_tg_pt_gp'):
++ print "#### ALUA Target Port Group"
++ print "echo " + tg_pt_gp_name + " > " + tg_pt_gp_file
++
++#FIXME: --aluasecmd support
++# print "lio_node --aluasecmd " + iqn + " " + tpgt + " " + lun
++
++ # Dump values of iscsi/iqn/tpgt/attrib/
++ print "#### Attributes for " + fabric_name + " Target Portal Group"
++ attrib_dir = fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/attrib/"
++ attrib_root = os.listdir(attrib_dir)
++ for attrib in attrib_root:
++ attrib_file = attrib_dir + attrib
++ p = os.open(attrib_file, 0)
++ value = os.read(p, 16)
++ print "echo " + value.rstrip() + " > " + attrib_file
++ os.close(p)
++
++ # Dump values for iscsi/iqn/tpgt/param
++ print "#### Parameters for " + fabric_name + " Target Portal Group"
++ param_dir = fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/param/"
++ param_root = os.listdir(param_dir)
++ for param in param_root:
++ param_file = param_dir + param
++ p = os.open(param_file, 0)
++ value = os.read(p, 256)
++ print "echo \"" + value.rstrip() + "\" > " + param_file
++ os.close(p)
++
++ if os.path.isfile(nexus_file):
++ continue
++
++ # Dump fabric Initiator Node ACLs from fabric_root/$WWN/tpgt_$TPGT/acls/
++ print "#### " + fabric_name + " Initiator ACLs for " + fabric_name + " Target Portal Group"
++ nacl_dir = fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/acls/"
++ nacl_root = os.listdir(nacl_dir)
++ for nacl in nacl_root:
++ print "mkdir -p " + nacl_dir + nacl
++
++ # Dump fabric Initiator ACL authentication info from fabric_root/$WWN/tpgt_$TPGT/acls//$INITIATOR/auth
++ print "#### " + fabric_name + " Initiator ACL authentication information"
++ auth_dir = nacl_dir + nacl + "/auth"
++ for auth in os.listdir(auth_dir):
++ if auth == "authenticate_target":
++ continue
++ auth_file = auth_dir + "/" + auth
++ p = os.open(auth_file, 0)
++ value = os.read(p, 256)
++ ret = value.isspace()
++ if ret:
++ os.close(p)
++ continue
++ print "echo -n " + value.rstrip() + " > " + auth_file
++ os.close(p)
++
++ # Dump fabric Initiator ACL TPG attributes from fabric_root/$WWN/tpgt_$TPGT/acls/$INITIATOR/attrib
++ print "#### " + fabric_name + " Initiator ACL TPG attributes"
++ nacl_attrib_dir = nacl_dir + nacl + "/attrib"
++ for nacl_attrib in os.listdir(nacl_attrib_dir):
++ nacl_attrib_file = nacl_attrib_dir + "/" + nacl_attrib
++ p = os.open(nacl_attrib_file, 0)
++ value = os.read(p, 8)
++ print "echo " + value.rstrip() + " > " + nacl_attrib_file
++ os.close(p)
++
++ # Dump fabric Initiator LUN ACLs from fabric_root/$WWN/tpgt_$TPGT//acls/$INITIATOR/lun
++ print "#### " + fabric_name + " Initiator LUN ACLs for iSCSI Target Portal Group"
++ lun_acl_dir = nacl_dir + nacl
++ for lun_acl in os.listdir(lun_acl_dir):
++ ret = re.search('lun_', lun_acl)
++ if not ret:
++ continue
++ lun_link_dir = nacl_dir + nacl + "/" + lun_acl
++ print "mkdir -p " + lun_link_dir
++
++ for lun_acl_link in os.listdir(lun_link_dir):
++ if lun_acl_link == "write_protect":
++ p = os.open(lun_link_dir + "/write_protect", 0)
++ value = os.read(p, 4)
++ print "echo " + value.rstrip() + " > " + lun_link_dir + "/write_protect"
++ os.close(p)
++ continue
++
++ if not os.path.islink(lun_link_dir + "/" + lun_acl_link):
++ continue
++
++ sourcelink = os.readlink(lun_link_dir + "/" + lun_acl_link)
++ sourcelink2 = os.path.join(os.path.dirname(lun_link_dir + "/" + lun_acl_link), sourcelink)
++ print "ln -s " + sourcelink2 + " " + lun_link_dir + "/" + lun_acl_link
++
++ # Dump value of fabric_root/$WWN/tpgt_$TPGT//enable
++ print "#### Trigger to enable " + fabric_name + " Target Portal Group"
++ enable_file = fabric_root + "/" + iqn + "/tpgt_" + tpgt + "/enable"
++ if os.path.isfile(enable_file):
++ p = os.open(enable_file, 0)
++ value = os.read(p, 1)
++ print "echo " + value.rstrip() + " > " + enable_file
++ os.close(p)
++
++
++ return
++
++def fabric_configfs_dump_all():
++
++ for fabric_name in os.listdir(target_root):
++ if fabric_name == "version":
++ continue
++ if fabric_name == "core":
++ continue
++ # FIXME: currently using lio_dump --stdout
++ if fabric_name == "iscsi":
++ continue
++
++ fabric_root = target_root + fabric_name
++# print "Using fabric_configfs_dump_all: " + fabric_name + ", " + fabric_root
++ module_name = fabric_get_module_name(fabric_name)
++# print "module_name: "+ module_name
++
++ fabric_configfs_dump(fabric_name, fabric_root, module_name);
++
++ return
++
++def fabric_backup_to_file(date_time, fabric_name, fabric_root, module_name):
++ now = date_time
++
++ if not os.path.isdir(fabric_root):
++ print "Unable to access fabric_root: " + fabric_root
++ sys.exit(1)
++
++ current_dir = "/etc/target"
++ backup_dir = "/etc/target/backup"
++ if not os.path.isdir(backup_dir):
++ op = "mkdir " + backup_dir
++ ret = os.system(op)
++ if ret:
++ print "Unable to open backup_dir"
++ sys.exit(1)
++
++ op = "tcm_fabric --stdout --fabric-name=" + fabric_name + " --fabric-root=" + fabric_root + " --module-name=" + module_name
++# print "Using op: " + op
++ p = sub.Popen(op, shell=True, stdout=sub.PIPE).stdout
++ if not p:
++ print "Unable to dump " + fabric_name + "/ConfigFS running state"
++ sys.exit(1)
++
++ orig_file = current_dir + "/" + fabric_name + "_start.sh"
++
++ print "Making backup of " + fabric_name + "/ConfigFS with timestamp: " + now
++ backup_file = backup_dir + "/" + fabric_name + "_backup-" + now + ".sh"
++ if os.path.isfile(backup_file):
++ print "" + fabric_name + " backup_file: " + backup_file + "already exists, exiting"
++ p.close()
++ sys.exit(1)
++
++ back = open(backup_file, 'w')
++
++ line = p.readline()
++ while line:
++ print >>back, line.rstrip()
++ line = p.readline()
++
++ p.close()
++ back.close()
++
++ ret = shutil.copyfile(backup_file, orig_file)
++ if ret:
++ print "Unable to copy " + back_file
++ sys.exit(1)
++
++ print "Successfully updated default config " + orig_file
++
++ return backup_file
++
++def fabric_backup_to_file_all(date_time):
++
++ if not os.path.isdir(target_root):
++ print "Unable to open target_root: " + target_root
++ sys.exit(1)
++
++ for fabric_name in os.listdir(target_root):
++ if fabric_name == "version":
++ continue
++ if fabric_name == "core":
++ continue
++ # FIXME: currently using lio_dump
++ if fabric_name == "iscsi":
++ continue
++
++ fabric_root = target_root + fabric_name
++# print "Using fabric_backup_to_file: " + date_time + ", " + fabric_name + ", " + fabric_root
++ module_name = fabric_get_module_name(fabric_name)
++# print "Using module_name: "+ module_name
++
++ fabric_backup_to_file(date_time, fabric_name, fabric_root, module_name)
++
++
++ return
++
++def fabric_unload(fabric_name, fabric_root, module_name):
++
++ if not os.path.isdir(fabric_root):
++ print "Unable to access fabric_root: " + fabric_root
++ sys.exit(1)
++
++ wwn_root = os.listdir(fabric_root)
++ for wwn in wwn_root:
++ if not os.path.isdir(fabric_root + "/" + wwn):
++ continue
++ if wwn == "discovery_auth":
++ continue
++
++ tpg_root = fabric_root + "/" + wwn
++ for tpgt_tmp in os.listdir(tpg_root):
++ if tpgt_tmp == "fabric_statistics":
++ continue
++
++ tpgt_tmp2 = tpgt_tmp.split('_')
++ tpgt = tpgt_tmp2[1]
++
++ if os.path.isfile(fabric_root + "/" + wwn + "/tpgt_" + tpgt + "/enable"):
++ disable_op = "echo 0 > " + fabric_root + "/" + wwn + "/tpgt_" + tpgt + "/enable"
++ ret = os.system(disable_op)
++ if ret:
++ print "Unable to disable TPG: " + wwn + " TPGT: " + tpgt
++
++ nacl_root = fabric_root + "/" + wwn + "/tpgt_" + tpgt + "/acls"
++ for nacl in os.listdir(nacl_root):
++ lun_acl_root = nacl_root + "/" + nacl + "/"
++ for lun_acl in os.listdir(lun_acl_root):
++ ret = re.search('lun_', lun_acl)
++ if not ret:
++ continue
++ mapped_lun = lun_acl[4:]
++
++ lun_link_dir = lun_acl_root + "/" + lun_acl + "/"
++ for lun_acl_link in os.listdir(lun_link_dir):
++ if lun_acl_link == "write_protect":
++ continue
++
++ if os.path.islink(lun_link_dir + "/" + lun_acl_link):
++ unlink_op = "unlink " + lun_link_dir + "/" + lun_acl_link
++ ret = os.system(unlink_op)
++ if ret:
++ print "Unable to unlink MappedLUN: " + lun_link_dir + "/" + lun_acl_link
++
++ dellunacl_op = "rmdir " + lun_link_dir
++ ret = os.system(dellunacl_op)
++ if ret:
++ print "Unable to rmdir fabric mapped_lun"
++
++ delnodeacl_op = "rmdir " + nacl_root + "/" + nacl + "/"
++ ret = os.system(delnodeacl_op)
++ if ret:
++ print "Unable to remove NodeACL: " + nacl_root + "/" + nacl + "/"
++
++ lun_root = fabric_root + "/" + wwn + "/tpgt_" + tpgt + "/lun"
++ for lun_tmp in os.listdir(lun_root):
++ lun_tmp2 = lun_tmp.split('_')
++ lun = lun_tmp2[1]
++
++ lun_dir = lun_root + "/lun_" + lun
++ for port in os.listdir(lun_dir):
++ if not os.path.islink(lun_dir + "/" + port):
++ continue
++
++ unlink_op = "unlink " + lun_dir + "/" + port
++ ret = os.system(unlink_op)
++ if ret:
++ print "Unable to unlink fabric port/lun"
++
++ rmdir_op= "rmdir " + lun_dir
++ ret = os.system(rmdir_op);
++ if ret:
++ print "Unable to rmdir fabric port/lun: " + lun_dir
++
++
++ rmdir_op = "rmdir " + fabric_root + "/" + wwn + "/tpgt_" + tpgt + "/"
++ ret = os.system(rmdir_op)
++ if ret:
++ print "Unable to rmdir fabric tpg: " + fabric_root + "/" + wwn + "/tpgt_" + tpgt + "/"
++
++ rmdir_op = "rmdir " + fabric_root + "/" + wwn + "/"
++ ret = os.system(rmdir_op)
++ if ret:
++ print "Unable to rmdir fabric wwn: " + fabric_root + "/" + wwn + "/"
++
++
++
++ rmdir_op = "rmdir " + fabric_root
++ ret = os.system(rmdir_op)
++ if ret:
++ print "Unable to release fabric_root: " + fabric_root
++
++ rmmod_op = "rmmod " + module_name
++ ret = os.system(rmmod_op)
++ if ret:
++ print "Unable to unload " + module_name
++
++ print "Successfully released fabric: " + fabric_root
++ return
++
++def fabric_get_module_name(fabric_name):
++ kernel_module = ""
++
++ for specs in os.listdir(spec_root):
++ if specs == "README":
++ continue
++# print "specs: " + specs + ", fabric_name: " + fabric_name
++
++ if not re.search(fabric_name + ".spec", specs) and not re.search("tcm_" + fabric_name + ".spec", specs) and not re.search(fabric_name, specs):
++ continue
++
++ op = "cat " + spec_root + specs
++ p = sub.Popen(op, shell=True, stdout=sub.PIPE).stdout
++ if not p:
++ print "Unable to dump " + fabric_name + "/ConfigFS running state"
++ sys.exit(1)
++
++ line = p.readline()
++ while line:
++ tmp = line.rstrip()
++ # Check for 'kernel_module' line in $FABRIC.spec
++ if re.search('kernel_module', tmp):
++ tmp_list = tmp.split('= ')
++ p.close()
++ return tmp_list[1]
++
++ line = p.readline()
++
++ p.close()
++
++ return kernel_module
++
++def fabric_unloadall():
++
++ module_name = ""
++
++ for fabric_name in os.listdir(target_root):
++ if fabric_name == "version":
++ continue
++ if fabric_name == "core":
++ continue
++ # FIXME: currently using lio_node --unload
++ if fabric_name == "iscsi":
++ continue
++
++ fabric_root = target_root + fabric_name
++ module_name = fabric_get_module_name(fabric_name)
++# print "fabric_get_module_name() using: " + module_name
++
++ if module_name == "":
++ continue
++
++ fabric_unload(fabric_name, fabric_root, module_name)
++
++
++def do_work(stdout_enable, stdout_enable_all, date_time, unload, unloadall, fabric_name, fabric_root, module_name):
++
++ if not stdout_enable == "None":
++ fabric_configfs_dump(fabric_name, fabric_root, module_name)
++ elif not stdout_enable_all == "None":
++ fabric_configfs_dump_all()
++ elif not date_time == "None":
++ fabric_backup_to_file(date_time, fabric_name, fabric_root, module_name)
++ elif not unload == "None":
++ fabric_unload(fabric_name, fabric_root, module_name)
++ elif not unloadall == "None":
++ fabric_unloadall()
++
++ return 0
++
++def main():
++
++ parser_fabric = optparse.OptionParser()
++ parser_fabric.add_option("--s","--stdout", dest='stdout_enable', action='store', nargs=0,
++ help="Dump running Fabric/ConfigFS syntax to STDOUT", type='string')
++ parser_fabric.add_option("--z","--stdoutall", dest='stdout_enable_all', action='store', nargs=0,
++ help="Dump all running Fabric/ConfigFS syntax to STDOUT", type='string')
++ parser_fabric.add_option("--t", "--tofile", dest="date_time", action='store', nargs=1,
++ help="Backup running Fabric/ConfigFS syntax to /etc/target/backup/fabricname_backup-<DATE_TIME>.sh",
++ type='string')
++ parser_fabric.add_option("--u", "--unload", dest="unload", action='store', nargs=0,
++ help="Unload running Fabric/ConfigFS", type='string')
++ parser_fabric.add_option("--a", "--unloadall", dest="unloadall", action='store', nargs=0,
++ help="Unload all running Fabric/ConfigFS", type='string')
++ parser_fabric.add_option("--f", "--fabric-name", dest='fabric_name', action='store', nargs=1,
++ help="Target fabric name", type='string')
++ parser_fabric.add_option("--r", "--fabric-root", dest='fabric_root', action='store', nargs=1,
++ help="Target fabric configfs root", type='string')
++ parser_fabric.add_option("--m", "--module-name", dest='module_name', action='store', nargs=1,
++ help="Target fabric module name ", type='string')
++
++ (opts_fabric, args_fabric) = parser_fabric.parse_args()
++
++ mandatories = ['fabric_name', 'fabric_root', 'module_name']
++ for m in mandatories:
++ if not opts_fabric.__dict__[m]:
++ unloadall = str(opts_fabric.__dict__['unloadall'])
++ stdout_enable = str(opts_fabric.__dict__['stdout_enable'])
++ stdout_enable_all = str(opts_fabric.__dict__['stdout_enable_all'])
++ date_time = str(opts_fabric.__dict__['date_time'])
++ if unloadall == "None" and stdout_enable == "None" and stdout_enable_all == "None" and date_time == "None":
++ print "mandatory option is missing\n"
++ parser_fabric.print_help()
++ exit(-1)
++
++ do_work(str(opts_fabric.stdout_enable), str(opts_fabric.stdout_enable_all),
++ str(opts_fabric.date_time), str(opts_fabric.unload), str(opts_fabric.unloadall),
++ str(opts_fabric.fabric_name), str(opts_fabric.fabric_root),
++ str(opts_fabric.module_name))
++
++if __name__ == "__main__":
++ main()
+diff --git a/targetcli/tcm_fileio.py b/targetcli/tcm_fileio.py
+new file mode 100644
+index 0000000..839d91a
+--- /dev/null
++++ b/targetcli/tcm_fileio.py
+@@ -0,0 +1,83 @@
++import os
++import subprocess as sub
++import string, re
++from optparse import OptionParser
++
++tcm_root = "/sys/kernel/config/target/core"
++
++def createvirtdev(path, params):
++
++# print "Calling fileio createvirtdev: path " + path
++ cfs_path = tcm_root + "/" + path + "/"
++# print "Calling fileio createvirtdev: params " + str(params)
++ fd_params = str(params)
++
++ # Extract the udev_dev path from fd_dev_name=
++ try:
++ off = fd_params.index('fd_dev_name=')
++ off += 12
++ file_tmp = fd_params[off:]
++ file = file_tmp.split(',')
++ except IOError, msg:
++ print "Unable to locate fd_dev_name= parameter key"
++ return -1
++
++ # Set UDEV path if struct file is pointing to an underlying struct block_device
++ if re.search('/dev/', file[0]):
++ udev_path = file[0]
++ set_udev_path_op = "echo -n " + udev_path + " > " + cfs_path + "udev_path"
++ ret = os.system(set_udev_path_op)
++ if ret:
++ print "pSCSI: Unable to set udev_path in " + cfs_path + " for: " + udev_path
++ return -1
++
++ control_opt = "echo -n " + params[0] + " > " + cfs_path + "control"
++# print "control_opt: " + control_opt
++ ret = os.system(control_opt)
++ if ret:
++ print "FILEIO: createvirtdev failed for control_opt with " + params[0]
++ return -1
++
++ enable_opt = "echo 1 > " + cfs_path + "enable"
++# print "Calling enable_opt " + enable_opt
++ ret = os.system(enable_opt)
++ if ret:
++ print "FILEIO: createvirtdev failed for enable_opt with " + params[0]
++ return -1
++
++def fd_freevirtdev():
++ pass
++
++def fd_get_params(path):
++ # Reference by udev_path if available
++ udev_path_file = path + "/udev_path"
++ p = os.open(udev_path_file, 0)
++ value = os.read(p, 1024)
++ if re.search('/dev/', value):
++ os.close(p)
++ # Append a FILEIO size of ' 0', as struct block_device sector count is autodetected by TCM
++ return "fd_dev_name=" + value.rstrip() + ",fd_dev_size=0"
++
++ os.close(p)
++
++ info_file = path + "/info"
++ p = open(info_file, 'rU')
++ try:
++ value = p.read(1024)
++ except IOError, msg:
++ p.close()
++ return
++ p.close()
++
++ off = value.index('File: ')
++ off += 6
++ fd_dev_name_tmp = value[off:]
++ fd_dev_name = fd_dev_name_tmp.split(' ')
++ off = value.index(' Size: ')
++ off += 7
++ fd_dev_size_tmp = value[off:]
++ fd_dev_size = fd_dev_size_tmp.split(' ')
++ params = "fd_dev_name=" + fd_dev_name[0] + ",fd_dev_size=" + fd_dev_size[0]
++
++ # fd_dev_name= and fd_dev_size= parameters for tcm_node --createdev
++ return params
+diff --git a/targetcli/tcm_iblock.py b/targetcli/tcm_iblock.py
+new file mode 100644
+index 0000000..f522dd2
+--- /dev/null
++++ b/targetcli/tcm_iblock.py
+@@ -0,0 +1,86 @@
++import os, tempfile
++import subprocess as sub
++import string, re
++from optparse import OptionParser
++
++tcm_root = "/sys/kernel/config/target/core"
++
++def createvirtdev(path, params):
++# print "Calling iblock createvirtdev: path " + path
++ cfs_path = tcm_root + "/" + path + "/"
++# print "Calling iblock createvirtdev: params " + str(params)
++ path = params[0]
++ if not re.search('/dev/', path):
++ print "IBLOCK: Please reference a valid /dev/ block_device"
++ return -1
++
++ udev_path = path.rstrip()
++ # Resolve symbolic links to get major/minor
++ udev_op = "/bin/ls -laL " + udev_path
++ p = sub.Popen(udev_op, shell=True, stdout=sub.PIPE).stdout
++ line = p.readline()
++ out = line.split(' ');
++ major = out[4]
++ minor = out[5]
++ p.close()
++
++ if major == "11,":
++ print "Unable to export Linux/SCSI TYPE_CDROM from IBLOCK, please use pSCSI export"
++ return -1
++ if major == "22,":
++ print "Unable to export IDE CDROM from IBLOCK"
++ return -1
++
++ set_udev_path_op = "echo -n " + udev_path + " > " + cfs_path + "udev_path"
++ ret = os.system(set_udev_path_op)
++ if ret:
++ print "IBLOCK: Unable to set udev_path in " + cfs_path + " for: " + udev_path
++ return -1
++
++ control_opt = "echo -n udev_path=" + udev_path + " > " + cfs_path + "control"
++ ret = os.system(control_opt)
++ if ret:
++ print "IBLOCK: createvirtdev failed for control_opt with " + control_opt
++ return -1
++
++ enable_opt = "echo 1 > " + cfs_path + "enable"
++ ret = os.system(enable_opt)
++ if ret:
++ print "IBLOCK: createvirtdev failed for enable_opt with " + enable_opt
++ return -1
++
++def iblock_freevirtdev():
++ pass
++
++def iblock_get_params(path):
++ # Reference by udev_path if available
++ udev_path_file = path + "/udev_path"
++ p = os.open(udev_path_file, 0)
++ value = os.read(p, 1024)
++ if re.search('/dev/', value):
++ os.close(p)
++ return value.rstrip()
++
++ os.close(p)
++
++ info_file = path + "/info"
++ p = open(info_file, 'rU')
++ try:
++ value = p.read(1024)
++ except IOError, msg:
++ p.close()
++ return
++ p.close()
++
++ of = value.index('Major: ')
++ off += 7
++ major_tmp = value[off:]
++ major = major_tmp.split(' ')
++ off = value.index('Minor: ')
++ off += 7
++ minor_tmp = value[off:]
++ minor = minor_tmp.split(' ')
++ params = "major=" + major[0] + ",minor=" + minor[0]
++ os.close(p)
++
++ return params
+diff --git a/targetcli/tcm_loop.py b/targetcli/tcm_loop.py
+new file mode 100644
+index 0000000..aeb211a
+--- /dev/null
++++ b/targetcli/tcm_loop.py
+@@ -0,0 +1,242 @@
++import os, sys
++import subprocess as sub
++import string
++import re
++from optparse import OptionParser
++
++tcm_loop_root = "/sys/kernel/config/target/loopback/"
++tcm_root = "/sys/kernel/config/target/core"
++
++def tcm_generate_naa_sas_address():
++ # Use NAA IEEE Registered Designator prefix, and append WWN UUID below
++ sas_address = "naa.6001405"
++
++ uuidgen_op = 'uuidgen'
++ p = sub.Popen(uuidgen_op, shell=True, stdout=sub.PIPE).stdout
++ uuid = p.readline()
++ p.close()
++
++ if not uuid:
++ print "Unable to generate UUID using uuidgen, continuing anyway"
++ sys.exit(1)
++
++ val = uuid.rstrip();
++ sas_address += val[:10]
++ sas_address = sas_address.replace('-','')
++
++ return sas_address
++
++def tcm_loop_add_target_www(option, opt_str, value, parser):
++ sas_target_address = tcm_generate_naa_sas_address();
++ sas_target_tpgt = str(value)
++
++def tcm_loop_del_target_wwn(option, opt_str, value, parser):
++ sas_target_address = str(value[0])
++ sas_target_tpgt = str(value[1])
++
++ tpgt_dir = tcm_loop_root + sas_target_address + "/tpgt_" + sas_target_tpgt
++ delete_op = "rmdir " + tpgt_dir
++ ret = os.system(delete_op)
++ if ret:
++ print "Unable to remove configfs group: " + tpgt_dir
++
++ naa_dir = tcm_loop_root + sas_target_address
++ delete_op = "rmdir " + naa_dir
++ ret = os.system(delete_op)
++ if ret:
++ print "Unable to remove configfs group: " + naa_dir
++ else:
++ print "Successfully removed NAA based SAS Target Address: " + naa_dir + "/" + tpgt_dir
++
++def tcm_loop_create_nexus(option, opt_str, value, parser):
++ sas_target_address = tcm_generate_naa_sas_address();
++ sas_target_tpgt = str(value)
++ sas_initiator_address = tcm_generate_naa_sas_address();
++
++ tpgt_dir = tcm_loop_root + sas_target_address + "/tpgt_" + sas_target_tpgt
++ create_op = "mkdir -p " + tpgt_dir
++ ret = os.system(create_op)
++ if ret:
++ print "Unable to create virtual Target Port: " + create_op
++ sys.exit(1)
++
++ # In TCM_Loop 4.x code, there is an nexus configfs attribute instead of
++ # nexus configfs group
++ nexus_dir = tpgt_dir + "/nexus"
++ if os.path.isfile(nexus_dir):
++ create_op = "echo " + sas_initiator_address + " > " + nexus_dir
++ else:
++ create_op = "mkdir -p " + nexus_dir + "/" + sas_initiator_address
++
++ ret = os.system(create_op)
++ if ret:
++ print "Unable to create virtual SAS I_T Nexus: " + create_op
++ sys.exit(1)
++ else:
++ print "Successfully created virtual SCSI I_T Nexus between TCM and Linux/SCSI HBA"
++ print " SAS Target Address: " + sas_target_address
++ print " SAS Initiator Address " + sas_initiator_address
++
++def tcm_loop_delete_nexus(option, opt_str, value, parser):
++ sas_target_address = str(value[0])
++ sas_target_tpgt = str(value[1])
++ sas_initiator_address = "";
++
++ nexus_dir = tcm_loop_root + sas_target_address + "/tpgt_" + sas_target_tpgt + "/nexus"
++
++ if os.path.isfile(nexus_dir):
++ delete_op = "echo NULL > " + nexus_dir
++
++ ret = os.system(delete_op)
++ if ret:
++ print "Unable to delete virtual SCSI I_T Nexus between TCM and Linux/SCSI HBA"
++ sys.exit(1)
++
++ print "Successfully deleted virtual SCSI I_T Nexus between TCM and Linux/SCSI HBA"
++ return
++
++ for nexus in os.listdir(nexus_dir):
++ delete_op = "rmdir " + nexus_dir + "/" + nexus
++
++ ret = os.system(delete_op)
++ if ret:
++ print "Unable to delete virtual SCSI I_T Nexus between TCM and Linux/SCSI HBA"
++ sys.exit(1)
++
++ print "Successfully deleted virtual SCSI I_T Nexus between TCM and Linux/SCSI HBA"
++ return
++
++def tcm_loop_addlun(option, opt_str, value, parser):
++ sas_target_address = str(value[0])
++ sas_target_tpgt = str(value[1])
++ sas_target_lun = str(value[2])
++
++ mkdir_op = "mkdir -p " + tcm_loop_root + sas_target_address + "/tpgt_" + sas_target_tpgt + "/lun/lun_" + sas_target_lun
++ ret = os.system(mkdir_op)
++ if ret:
++ print "Unable to create SAS Target Port LUN configfs group: " + mkdir_op
++ sys.exit(1)
++
++ tcm_obj = str(value[3]);
++ port_src = tcm_root + "/" + tcm_obj
++ port_dst = tcm_loop_root + sas_target_address + "/tpgt_" + sas_target_tpgt + "/lun/lun_" + sas_target_lun + "/virtual_scsi_port"
++
++ link_op = "ln -s " + port_src + " " + port_dst
++ ret = os.system(link_op)
++ if not ret:
++ print "Successfully created SAS Target Port to local virtual SCSI Logical Unit"
++ # FIXME Add tcm_loop_alua_check_secondary_md()
++ # FIXME Add tcm_loop_alua_set_secondary_write_md()
++ else:
++ print "Unable to create SAS Target Port to local virtual SCSI Logical Unit"
++ sys.exit(1)
++
++def tcm_loop_dellun(option, opt_str, value, parser):
++ sas_target_address = str(value[0])
++ sas_target_tpgt = str(value[1])
++ sas_target_lun = str(value[2])
++
++ port_link = ""
++
++ lun_dir = tcm_loop_root + sas_target_address + "/tpgt_" + sas_target_tpgt + "/lun/lun_" + sas_target_lun
++ if not os.path.isdir(lun_dir):
++ print "TCM_Loop lun_dir: " + lun_dir + " does not exist"
++ sys.exit(1)
++
++ # Locate the port symlink, skipping over the per TCM port alua_* attributes
++ for port in os.listdir(lun_dir):
++ port_link_tmp = lun_dir + "/" + port
++ if not os.path.islink(port_link_tmp):
++ continue
++
++ port_link = port_link_tmp
++ break
++
++ if port_link == "":
++ print "Active TCM_Loop port link does not exist!"
++ sys.exit(1)
++
++ unlink_op = "unlink " + port_link
++ ret = os.system(unlink_op)
++ if ret:
++ print "Unable to unlink port for virtual SCSI Logical Unit: " + port
++ sys.exit(1)
++
++ rmdir_op = "rmdir " + tcm_loop_root + sas_target_address + "/tpgt_" + sas_target_tpgt + "/lun/lun_" + sas_target_lun
++ ret = os.system(rmdir_op)
++ if ret:
++ print "Unable to rmdir configfs group for virtual SCSI Logical Unit: " + port
++ sys.exit(1)
++ else:
++ print "Succesfully deleted local virtual SCSI Logical Unit from SAS Target Port"
++
++def tcm_loop_unload(option, opt_str, value, parser):
++
++ for sas_target_naa in os.listdir(tcm_loop_root):
++ print "sas_target_naa: " + sas_target_naa
++
++ if os.path.isfile(tcm_loop_root + sas_target_naa) == True:
++ continue
++
++ tpgt_dir = tcm_loop_root + sas_target_naa + "/"
++ for sas_target_tpgt in os.listdir(tpgt_dir):
++ if sas_target_tpgt == "fabric_statistics":
++ continue
++
++ print "sas_target_tpgt: " + sas_target_tpgt
++
++ lun_dir = tpgt_dir + "/" + sas_target_tpgt + "/lun/"
++ for sas_target_lun in os.listdir(lun_dir):
++
++ print "sas_target_lun: " + sas_target_lun
++ tpgt = sas_target_tpgt[5:]
++ lun = sas_target_lun[4:]
++ vals = [sas_target_naa, tpgt, lun]
++ tcm_loop_dellun(None, None, vals, None)
++
++ tpgt = sas_target_tpgt[5:]
++ vals = [sas_target_naa, tpgt]
++
++ tcm_loop_delete_nexus(None, None, vals, None)
++
++ tcm_loop_del_target_wwn(None, None, vals, None)
++
++ rmdir_op = "rmdir " + tcm_loop_root
++ ret = os.system(rmdir_op)
++ if ret:
++ print "Unable to remove tcm_loop_root configfs group: " + tcm_loop_root
++ sys.exit(1)
++
++ rmmod_op = "rmmod tcm_loop"
++ ret = os.system(rmmod_op)
++ if ret:
++ print "Unable to remove tcm_loop kernel module"
++ sys.exit(1)
++
++ print "Successfully removed tcm_loop kernel module"
++
++def main():
++
++ parser = OptionParser()
++ parser.add_option("--delwwn", action="callback", callback=tcm_loop_del_target_wwn, nargs=2,
++ type="string", dest="NAA_TARGET_WWN TPGT", help="Delete a SAS Virtual HBA by WWN+TPGT")
++ parser.add_option("--createnexus", action="callback", callback=tcm_loop_create_nexus, nargs=1,
++ type="string", dest="TPGT", help="Create a virtual SAS I_T Nexus using generated NAA WWN for SAS Address. This will create a new Linux/SCSI Host Bus Adapter for the I_T Nexus");
++ parser.add_option("--delnexus", action="callback", callback=tcm_loop_delete_nexus, nargs=2,
++ type="string", dest="NAA_TARGET_WWN TPGT", help="Delete a virtual SAS I_T Nexus");
++ parser.add_option("--addlun", action="callback", callback=tcm_loop_addlun, nargs=4,
++ type="string", dest="NAA_TARGET_WWN TPGT LUN HBA/DEV", help="Add virtual SCSI Linux to NAA Target/Initiator Sas Addresses")
++ parser.add_option("--dellun", action="callback", callback=tcm_loop_dellun, nargs=3,
++ type="string", dest="NAA_TARGET_WWN TPGT LUN", help="Delete Target SAS Port to virtual SCSI Logical unit mapping")
++ parser.add_option("--unload", action="callback", callback=tcm_loop_unload, nargs=0,
++ help="Shutdown all virtual SCSI LUNs and unload tcm_loop")
++
++ (options, args) = parser.parse_args()
++ if len(sys.argv) == 1:
++ parser.print_help()
++ sys.exit(0)
++ elif not re.search('--', sys.argv[1]):
++ lio_err("Unknown CLI option: " + sys.argv[1])
++
++if __name__ == "__main__":
++ main()
+diff --git a/targetcli/tcm_node.py b/targetcli/tcm_node.py
+new file mode 100644
+index 0000000..9df6f40
+--- /dev/null
++++ b/targetcli/tcm_node.py
+@@ -0,0 +1,737 @@
++from __future__ import with_statement
++
++import os, sys, signal
++import subprocess as sub
++import string
++import re
++import errno
++import uuid
++import shutil
++from optparse import OptionParser
++
++import tcm_pscsi
++import tcm_iblock
++import tcm_ramdisk
++import tcm_fileio
++
++tcm_root = "/sys/kernel/config/target/core"
++
++def tcm_err(msg):
++ print >> sys.stderr, msg
++ sys.exit(1)
++
++def tcm_read(filename):
++ with open(filename) as f:
++ return f.read()
++
++def tcm_write(filename, value, newline=True):
++ with open(filename, "w") as f:
++ f.write(value)
++ if newline:
++ f.write("\n")
++
++def tcm_full_path(arg):
++ return tcm_root + "/" + arg
++
++def tcm_check_dev_exists(dev_path):
++ full_path = tcm_full_path(dev_path)
++ if not os.path.isdir(full_path):
++ tcm_err("TCM/ConfigFS storage object does not exist: " + full_path)
++
++def tcm_add_alua_lugp(gp_name):
++ os.makedirs(tcm_root + "/alua/lu_gps/" + gp_name)
++
++ try:
++ tcm_write(tcm_root + "/alua/lu_gps/%s/lu_gp_id" % lu_gp_name, lu_gp_name)
++ except:
++ os.rmdir(tcm_root + "/alua/lu_gps/" + lu_gp_name)
++ raise
++
++def tcm_add_alua_tgptgp(dev_path, gp_name):
++ tcm_check_dev_exists(dev_path)
++
++ alua_cfs_path = tcm_full_path(dev_path) + "alua/" + gp_name + "/"
++
++ os.makedirs(alua_cfs_path)
++
++ try:
++ tcm_write(alua_cfs_path + "tg_pt_gp_id", "0")
++ except:
++ os.rmdir(alua_cfs_path)
++ raise
++
++def tcm_alua_check_metadata_dir(dev_path):
++ alua_path = "/var/target/alua/tpgs_" + tcm_get_unit_serial(dev_path) + "/"
++ if os.path.isdir(alua_path):
++ return
++
++ # Create the ALUA metadata directory for the passed storage object
++ # if it does not already exist.
++ os.makedirs(alua_path)
++
++def tcm_alua_delete_metadata_dir(unit_serial):
++ try:
++ os.rmdir("/var/target/alua/tpgs_" + unit_serial + "/")
++ except OSError:
++ pass
++
++def tcm_alua_process_metadata(dev_path, gp_name, gp_id):
++ alua_gp_path = tcm_full_path(dev_path) + "/alua/" + gp_name
++ alua_md_path = "/var/target/alua/tpgs_" + tcm_get_unit_serial(dev_path) \
++ + "/" + gp_name
++
++ if not os.path.isfile(alua_md_path):
++ # If not pre-existing ALUA metadata exists, go ahead and
++ # allow new ALUA state changes to create and update the
++ # struct file metadata
++ tcm_write(alua_gp_path + "/alua_write_metadata", "1")
++ return
++
++ with open(alua_md_path, 'rU') as p:
++ d = dict()
++ for line in p.readlines():
++ name, value = line.split("=")
++ d[name.strip()] = value.strip()
++
++ if "tg_pt_gp_id" in d and int(d["tg_pt_gp_id"]) != int(gp_id):
++ raise IOError("Passed tg_pt_gp_id: %s does not match extracted: %s" % \
++ (gp_id, d["tg_pt_gp_id"]))
++
++ if "alua_access_state" in d:
++ tcm_write(alua_gp_path + "/alua_access_state", d["alua_access_state"])
++
++ if "alua_access_status" in d:
++ tcm_write(alua_gp_path + "/alua_access_status", d["alua_access_status"])
++
++ # Now allow changes to ALUA target port group update the struct file metadata
++ # in /var/target/alua/tpgs_$T10_UNIT_SERIAL/$TG_PT_GP_NAME
++ tcm_write(alua_gp_path + "/alua_write_metadata", "1")
++
++def tcm_add_alua_tgptgp_with_md(dev_path, gp_name, gp_id):
++ alua_gp_path = tcm_full_path(dev_path) + "/alua/" + gp_name
++
++ tcm_check_dev_exists(dev_path)
++
++ # If the default_tg_pt_gp is passed, we skip the creation (as it already exists)
++ # and just process ALUA metadata
++ if gp_name == 'default_tg_pt_gp' and gp_id == '0':
++ tcm_alua_process_metadata(dev_path, gp_name, gp_id)
++ return
++
++ os.makedirs(alua_gp_path)
++
++ try:
++ tcm_write(alua_gp_path + "/tg_pt_gp_id", gp_id)
++ except:
++ os.rmdir(alua_gp_path)
++ raise
++
++ # Now process the ALUA metadata for this group
++ tcm_alua_process_metadata(dev_path, gp_name, gp_id)
++
++def tcm_delhba(hba_name):
++ hba_path = tcm_full_path(hba_name)
++
++ for g in os.listdir(hba_path):
++ if g == "hba_info" or g == "hba_mode":
++ continue
++
++ __tcm_freevirtdev(hba_name + "/" + g)
++
++ os.rmdir(hba_path)
++
++def tcm_del_alua_lugp(lu_gp_name):
++ if not os.path.isdir(tcm_root + "/alua/lu_gps/" + lu_gp_name):
++ tcm_err("ALUA Logical Unit Group: " + lu_gp_name + " does not exist!")
++
++ os.rmdir(tcm_root + "/alua/lu_gps/" + lu_gp_name)
++
++def __tcm_del_alua_tgptgp(dev_path, gp_name):
++ tcm_check_dev_exists(dev_path)
++
++ full_path = tcm_full_path(dev_path)
++
++ if not os.path.isdir(full_path + "/alua/" + gp_name):
++ tcm_err("ALUA Target Port Group: " + gp_name + " does not exist!")
++
++ os.rmdir(full_path + "/alua/" + gp_name)
++
++# deletes configfs entry for alua *and* metadata dir.
++def tcm_del_alua_tgptgp(dev_path, gp_name):
++ tcm_check_dev_exists(dev_path)
++
++ alua_md_path = "/var/target/alua/tpgs_" + tcm_get_unit_serial(dev_path) \
++ + "/" + gp_name
++
++ __tcm_del_alua_tgptgp(dev_path, gp_name)
++
++ if not os.path.isfile(alua_md_path):
++ return
++
++ shutil.rmtree(alua_md_path)
++
++def tcm_generate_uuid_for_unit_serial(dev_path):
++ # Generate random uuid
++ tcm_set_wwn_unit_serial(dev_path, str(uuid.uuid4()))
++
++tcm_types = ( \
++ dict(name="pscsi", module=tcm_pscsi, gen_uuid=False),
++ dict(name="stgt", module=None, gen_uuid=True),
++ dict(name="iblock", module=tcm_iblock, gen_uuid=True),
++ dict(name="rd_dr", module=tcm_ramdisk, gen_uuid=True),
++ dict(name="rd_mcp", module=tcm_ramdisk, gen_uuid=True),
++ dict(name="fileio", module=tcm_fileio, gen_uuid=True),
++)
++
++def tcm_createvirtdev(dev_path, plugin_params, establishdev=False):
++ hba_path = dev_path.split('/')[0]
++
++ # create hba if it doesn't exist
++ hba_full_path = tcm_full_path(hba_path)
++ if not os.path.isdir(hba_full_path):
++ os.mkdir(hba_full_path)
++
++ # create dev if it doesn't exist
++ full_path = tcm_full_path(dev_path)
++ if os.path.isdir(full_path):
++ tcm_err("TCM/ConfigFS storage object already exists: " + full_path)
++ else:
++ os.mkdir(full_path)
++
++ # Determine if --establishdev is being called and we want to skip
++ # the T10 Unit Serial Number generation
++ gen_uuid = True
++ if establishdev:
++ gen_uuid = False
++
++ # Calls into submodules depending on target_core_mod subsystem plugin
++ for tcm in tcm_types:
++ if hba_path.startswith(tcm["name"] + "_"):
++ try:
++ if tcm["module"]:
++ # modules expect plugin_params to be a list, for now.
++ tcm["module"].createvirtdev(dev_path, [plugin_params])
++ else:
++ tcm_err("no module for %s" % tcm["name"])
++ except:
++ os.rmdir(full_path)
++ print "Unable to register TCM/ConfigFS storage object: " \
++ + full_path
++ raise
++
++ print tcm_read(full_path + "/info")
++
++ if tcm["gen_uuid"] and gen_uuid:
++ tcm_generate_uuid_for_unit_serial(dev_path)
++ tcm_alua_check_metadata_dir(dev_path)
++ break
++
++def tcm_get_unit_serial(dev_path):
++ string = tcm_read(tcm_full_path(dev_path) + "/wwn/vpd_unit_serial")
++ return string.split(":")[1].strip()
++
++def tcm_show_aptpl_metadata(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ aptpl_file = "/var/target/pr/aptpl_" + tcm_get_unit_serial(dev_path)
++ if not os.path.isfile(aptpl_file):
++ tcm_err("Unable to dump PR APTPL metadata file: " + aptpl_file)
++
++ print tcm_read(aptpl_file)
++
++def tcm_delete_aptpl_metadata(unit_serial):
++ aptpl_file = "/var/target/pr/aptpl_" + unit_serial
++ if not os.path.isfile(aptpl_file):
++ return
++
++ shutil.rmtree(aptpl_file)
++
++def tcm_process_aptpl_metadata(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ full_path = tcm_full_path(dev_path)
++
++ aptpl_file = "/var/target/pr/aptpl_" + tcm_get_unit_serial(dev_path)
++ if not os.path.isfile(aptpl_file):
++ return
++
++ # read PR info from file
++ lines = tcm_read(aptpl_file).split()
++
++ if not lines[0].startswith("PR_REG_START:"):
++ return
++
++ reservations = []
++ for line in lines:
++ if line.startswith("PR_REG_START:"):
++ res_list = []
++ elif line.startswith("PR_REG_END:"):
++ reservations.append(res_list)
++ else:
++ res_list.append(line.strip())
++
++ # write info into configfs
++ for res in reservations:
++ tcm_write(full_path + "/pr/res_aptpl_metadata", ",".join(res))
++
++def tcm_establishvirtdev(dev_path, plugin_params):
++ tcm_createvirtdev(dev_path, plugin_params, True)
++
++def tcm_create_pscsi(dev_path, ctl):
++ # convert passed 3-tuple to format pscsi expects
++ # "1:3:5" -> "scsi_channel_id=1,scsi_target_id=3..."
++ #
++ param_names = ("scsi_channel_id", "scsi_target_id", "scsi_lun_id")
++
++ pscsi_params = zip(param_names, ctl.split(":"))
++ pscsi_params_str = ",".join([x + "=" + y for x, y in pscsi_params])
++
++ tcm_createvirtdev(dev_path, pscsi_params_str)
++
++def tcm_create_pscsibyudev(dev_path, udev_path):
++ tcm_createvirtdev(cfs_dev, udev_path)
++
++def tcm_create_iblock(dev_path, udev_path):
++ tcm_createvirtdev(dev_path, udev_path)
++
++def tcm_create_fileio(dev_path, filename, size):
++ fileio_params = "fd_dev_name=" + filename + ",fd_dev_size=" + size
++ tcm_createvirtdev(dev_path, fileio_params)
++
++def tcm_create_ramdisk(dev_path, pages):
++ tcm_createvirtdev(dev_path, pages)
++
++def __tcm_freevirtdev(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ full_path = tcm_full_path(dev_path)
++
++ for tg_pt_gp in os.listdir(full_path + "/alua/"):
++ if tg_pt_gp == "default_tg_pt_gp":
++ continue
++ __tcm_del_alua_tgptgp(dev_path, tg_pt_gp)
++
++ os.rmdir(full_path)
++
++def tcm_freevirtdev(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ unit_serial = tcm_get_unit_serial(dev_path)
++
++ __tcm_freevirtdev(dev_path)
++ # For explict tcm_node --freedev, delete any remaining
++ # PR APTPL and ALUA metadata
++ tcm_delete_aptpl_metadata(unit_serial)
++ tcm_alua_delete_metadata_dir(unit_serial)
++
++def tcm_list_dev_attribs(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ full_path = tcm_full_path(dev_path)
++
++ print "TCM Storage Object Attributes for " + full_path
++ for attrib in os.listdir(full_path + "/attrib/"):
++ print " %s: %s" % \
++ (attrib, tcm_read(full_path + "/attrib/" + attrib).strip())
++
++def tcm_list_hbas():
++ for hba in os.listdir(tcm_root):
++ if hba == "alua":
++ continue
++
++ print "\------> " + hba
++ dev_root = tcm_root + "/" + hba
++ print "\t" + tcm_read(dev_root+"/hba_info").strip()
++
++ for dev in os.listdir(dev_root):
++ if dev in ("hba_info", "hba_mode"):
++ continue
++
++ try:
++ value = tcm_read(dev_root + "/" + dev + "/info")
++ except IOError, msg:
++ print " \-------> " + dev
++ print " No TCM object association active, skipping"
++ continue
++
++ udev_path = tcm_read(dev_root + "/" + dev + "/udev_path")
++ if udev_path:
++ udev_str = "udev_path: " + udev_path.rstrip()
++ else:
++ udev_str = "udev_path: N/A"
++
++ print " \-------> " + dev
++ print " " + value.rstrip()
++ print " " + udev_str
++
++def tcm_list_alua_lugps():
++ for lu_gp in os.listdir(tcm_root + "/alua/lu_gps"):
++ group_path = tcm_root + "/alua/lu_gps/" + lu_gp
++ lu_gp_id = tcm_read(group_path + "/lu_gp_id").strip()
++ print "\------> " + lu_gp + " LUN Group ID: " + lu_gp_id
++
++ lu_gp_members = tcm_read(group_path + "/members").strip().split()
++
++ if not lu_gp_members:
++ print " No Logical Unit Group Members"
++ continue
++
++ for member in lu_gp_members:
++ print " " + member
++
++def tcm_dump_alua_state(alua_state_no):
++ if alua_state_no == "0":
++ return "Active/Optimized"
++ elif alua_state_no == "1":
++ return "Active/NonOptimized"
++ elif alua_state_no == "2":
++ return "Standby"
++ elif alua_state_no == "3":
++ return "Unavailable"
++ elif alua_state_no == "15":
++ return "Transition"
++ else:
++ return "Unknown"
++
++def tcm_list_alua_tgptgp(dev_path, gp_name):
++ tcm_check_dev_exists(dev_path)
++
++ gp_path = tcm_full_path(dev_path) + "/alua/" + gp_name
++
++ gp_id = tcm_read(gp_path + "/tg_pt_gp_id").strip()
++ print "\------> " + tg_pt_gp + " Target Port Group ID: " + tg_pt_gp_id
++
++ alua_type = tcm_read(gp_path + "/alua_access_type").strip()
++ print " Active ALUA Access Type(s): " + alua_type
++
++ alua_state = tcm_read(gp_path + "/alua_access_state").strip()
++ print " Primary Access State: " + tcm_dump_alua_state(alua_state)
++
++ try:
++ access_status = tcm_read(gp_path + "/alua_access_status").strip()
++ print " Primary Access Status: " + access_status
++ except IOError:
++ pass
++
++ preferred = tcm_read(gp_path + "/preferred").strip()
++ print " Preferred Bit: " + preferred
++
++ nonop_delay = tcm_read(gp_path + "/nonop_delay_msecs").strip()
++ print " Active/NonOptimized Delay in milliseconds: " + nonop_delay
++
++ trans_delay = tcm_read(gp_path + "/trans_delay_msecs").strip()
++ print " Transition Delay in milliseconds: " + trans_delay
++
++ gp_members = tcm_read(gp_path + "/members").strip().split()
++
++ print " \------> TG Port Group Members"
++ if not gp_members:
++ print " No Target Port Group Members"
++ else:
++ for member in gp_members:
++ print " " + member
++
++def tcm_list_alua_tgptgps(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ for tg_pt_gp in os.listdir(tcm_full_path(dev_path) + "/alua/"):
++ tcm_list_alua_tgptgp(dev_path, tg_pt_gp)
++
++def tcm_show_persistent_reserve_info(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ full_path = tcm_full_path(dev_path)
++
++ for f in os.listdir(full_path + "/pr/"):
++ info = tcm_read(full_path + "/pr/" + f).strip()
++ if info:
++ print info
++
++def tcm_set_alua_state(dev_path, gp_name, access_state):
++ tcm_check_dev_exists(dev_path)
++
++ new_alua_state_str = str(access_state).lower()
++
++ if new_alua_state_str == "o":
++ alua_state = 0 # Active/Optimized
++ elif new_alua_state_str == "a":
++ alua_state = 1 # Active/NonOptimized
++ elif new_alua_state_str == "s":
++ alua_state = 2 # Standby
++ elif new_alua_state_str == "u":
++ alua_state = 3 # Unavailable
++ else:
++ tcm_err("Unknown ALUA access state: " + new_alua_state_str)
++
++ alua_path = tcm_full_path(dev_path) + "/alua/" + gp_name + "/alua_access_state"
++ tcm_write(alua_path, str(alua_state))
++
++def tcm_set_alua_type(dev_path, gp_name, access_type):
++ tcm_check_dev_exists(dev_path)
++
++ new_alua_type_str = str(access_type).lower()
++
++ if new_alua_type_str == "both":
++ alua_type = 3
++ elif new_alua_type_str == "explict":
++ alua_type = 2
++ elif new_alua_type_str == "implict":
++ alua_type = 1
++ elif new_alua_type_str == "none":
++ alua_type = 0
++ else:
++ tcm_err("Unknown ALUA access type: " + new_alua_type_str)
++
++ alua_path = tcm_full_path(dev_path) + "/alua/" + gp_name + "/alua_access_type"
++ tcm_write(alua_path, str(alua_type))
++
++def tcm_set_alua_nonop_delay(dev_path, gp_name, msec_delay):
++ tcm_check_dev_exists(dev_path)
++
++ if not os.path.isdir(tcm_full_path(dev_path) + "/alua/" + gp_name):
++ tcm_err("Unable to locate TG Pt Group: " + gp_name)
++
++ alua_path = tcm_full_path(dev_path) + "/alua/" + gp_name + "/nonop_delay_msecs"
++ tcm_write(alua_path, str(msec_delay))
++
++def tcm_set_alua_trans_delay(dev_path, gp_name, msec_delay):
++ tcm_check_dev_exists(dev_path)
++
++ if not os.path.isdir(tcm_full_path(dev_path) + "/alua/" + gp_name):
++ tcm_err("Unable to locate TG Pt Group: " + gp_name)
++
++ alua_path = tcm_full_path(dev_path) + "/alua/" + gp_name + "/trans_delay_msecs"
++ tcm_write(alua_path, str(msec_delay))
++
++def tcm_clear_alua_tgpt_pref(dev_path, gp_name):
++ tcm_check_dev_exists(dev_path)
++
++ if not os.path.isdir(tcm_full_path(dev_path) + "/alua/" + gp_name):
++ tcm_err("Unable to locate TG Pt Group: " + gp_name)
++
++ alua_path = tcm_full_path(dev_path) + "/alua/" + gp_name + "/preferred"
++ tcm_write(alua_path, "0")
++
++def tcm_set_alua_tgpt_pref(dev_path, gp_name):
++ tcm_check_dev_exists(dev_path)
++
++ if not os.path.isdir(tcm_full_path(dev_path) + "/alua/" + gp_name):
++ tcm_err("Unable to locate TG Pt Group: " + gp_name)
++
++ alua_path = tcm_full_path(dev_path) + "/alua/" + gp_name + "/preferred"
++ tcm_write(alua_path, "1")
++
++def tcm_set_alua_lugp(dev_path, gp_name):
++ tcm_check_dev_exists(dev_path)
++
++ if not os.path.isdir(tcm_full_path(dev_path) + "/alua/lu_gps/" + gp_name):
++ tcm_err("Unable to locate ALUA Logical Unit Group: " + gp_name)
++
++ alua_path = tcm_full_path(dev_path) + "/alua/lu_gps/" + gp_name + "/alua_lu_gp"
++ tcm_write(alua_path, gp_name)
++
++def tcm_set_dev_attrib(dev_path, attrib, value):
++ tcm_check_dev_exists(dev_path)
++
++ tcm_write(tcm_full_path(dev_path) + "/attrib/" + attrib, value)
++
++def tcm_set_udev_path(dev_path, udev_path):
++ tcm_check_dev_exists(dev_path)
++
++ tcm_write(tcm_full_path(dev_path) + "/udev_path", udev_path, newline=False)
++
++def tcm_set_wwn_unit_serial(dev_path, unit_serial):
++ tcm_check_dev_exists(dev_path)
++
++ tcm_write(tcm_full_path(dev_path) + "/wwn/vpd_unit_serial", unit_serial)
++
++def tcm_set_wwn_unit_serial_with_md(dev_path, unit_serial):
++ tcm_check_dev_exists(dev_path)
++
++ tcm_set_wwn_unit_serial(dev_path, unit_serial)
++ # Process PR APTPL metadata
++ tcm_process_aptpl_metadata(dev_path)
++ # Make sure the ALUA metadata directory exists for this storage object
++ tcm_alua_check_metadata_dir(dev_path)
++
++def tcm_show_udev_path(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ print tcm_read(tcm_full_path(dev_path) + "/udev_path")
++
++def tcm_show_wwn_info(dev_path):
++ tcm_check_dev_exists(dev_path)
++
++ full_path = tcm_full_path(dev_path) + "/wwn/"
++
++ for f in os.listdir(full_path):
++ info = tcm_read(full_path + f).strip()
++ if info:
++ print info
++
++def tcm_unload():
++ if not os.path.isdir(tcm_root):
++ tcm_err("Unable to access tcm_root: " + tcm_root)
++
++ hba_root = os.listdir(tcm_root)
++
++ for f in hba_root:
++ if f == "alua":
++ continue
++
++ tcm_delhba(f)
++
++ for lu_gp in os.listdir(tcm_root + "/alua/lu_gps"):
++ if lu_gp == "default_lu_gp":
++ continue
++
++ tcm_del_alua_lugp(lu_gp)
++
++ # Unload TCM subsystem plugin modules
++ for module in ("iblock", "file", "pscsi", "stgt"):
++ os.system("rmmod target_core_%s" % module)
++
++ # Unload TCM Core
++ rmmod_op = "rmmod target_core_mod"
++ ret = os.system(rmmod_op)
++ if ret:
++ tcm_err("Unable to rmmod target_core_mod")
++
++def tcm_version():
++ return tcm_read("/sys/kernel/config/target/version").strip()
++
++cmdline_options = ( \
++ dict(opt_str="--addlungp", callback=tcm_add_alua_lugp, nargs=1,
++ dest="lu_gp_name", help="Add ALUA Logical Unit Group"),
++ dict(opt_str=("--addtgptgp","--addaluatpg"),
++ callback=tcm_add_alua_tgptgp, nargs=2,
++ dest="HBA/DEV <TG_PT_GP_NAME>",
++ help="Add ALUA Target Port Group to Storage Object"),
++ dict(opt_str=("--addtgptgpwithmd","--addaluatpgwithmd"), action="callback",
++ callback=tcm_add_alua_tgptgp_with_md, nargs=3,
++ dest="HBA/DEV <TG_PT_GP_NAME> <TG_PT_GP_ID>",
++ help="Add ALUA Target Port Group to Storage Object with ID and process ALUA metadata"),
++ dict(opt_str=("--block","--iblock"), callback=tcm_create_iblock, nargs=2,
++ dest="HBA/DEV <UDEV_PATH>",
++ help="Associate TCM/IBLOCK object with Linux/BLOCK device"),
++ dict(opt_str="--clearaluapref", callback=tcm_clear_alua_tgpt_pref,
++ nargs=2, dest="HBA/DEV <TG_PT_GP_NAME>",
++ help="Clear ALUA Target Port Group Preferred Bit"),
++ dict(opt_str="--delhba", callback=tcm_delhba, nargs=1,
++ dest="HBA", help="Delete TCM Host Bus Adapter (HBA)"),
++ dict(opt_str="--dellungp", callback=tcm_del_alua_lugp, nargs=1,
++ dest="lu_gp_name", help="Delete ALUA Logical Unit Group"),
++ dict(opt_str=("--deltgptgp","--delaluatpg"), callback=tcm_del_alua_tgptgp, nargs=2,
++ dest="HBA/DEV TG_PT_GP_NAME",
++ help="Delete ALUA Target Port Group from Storage Object"),
++ dict(opt_str="--createdev", callback=tcm_createvirtdev, nargs=2,
++ dest="HBA/DEV <SUBSYSTEM_PARAMS>",
++ help="Create TCM Storage Object using subsystem dependent parameters,"
++ " and generate new T10 Unit Serial for IBLOCK,FILEIO,RAMDISK"),
++ dict(opt_str="--establishdev", callback=tcm_establishvirtdev, nargs=2,
++ dest="HBA/DEV <SUBSYSTEM_PARAMS>",
++ help="Create TCM Storage Object using subsystem dependent parameters, do"
++ "not generate new T10 Unit Serial"),
++ dict(opt_str="--fileio", callback=tcm_create_fileio, nargs=3,
++ dest="HBA/DEV <FILE> <SIZE_IN_BYTES>",
++ help="Associate TCM/FILEIO object with Linux/VFS file or underlying"
++ " device for buffered FILEIO"),
++ dict(opt_str="--freedev", callback=tcm_freevirtdev, nargs=1,
++ dest="HBA/DEV", help="Free TCM Storage Object"),
++ dict(opt_str="--listdevattr", callback=tcm_list_dev_attribs, nargs=1,
++ dest="HBA/DEV", help="List TCM storage object device attributes"),
++ dict(opt_str="--listhbas", callback=tcm_list_hbas, nargs=0,
++ help="List TCM Host Bus Adapters (HBAs)"),
++ dict(opt_str="--listlugps", callback=tcm_list_alua_lugps, nargs=0,
++ help="List ALUA Logical Unit Groups"),
++ dict(opt_str=("--listtgptgp","--listaluatpg"), callback=tcm_list_alua_tgptgp, nargs=2,
++ dest="HBA/DEV <TG_PT_GP_NAME>",
++ help="List specific ALUA Target Port Group for Storage Object"),
++ dict(opt_str=("--listtgptgps","--listaluatpgs"), callback=tcm_list_alua_tgptgps, nargs=1,
++ dest="HBA/DEV", help="List all ALUA Target Port Groups for Storage Object"),
++ dict(opt_str="--pr", callback=tcm_show_persistent_reserve_info, nargs=1,
++ dest="HBA/DEV", help="Show Persistent Reservation info"),
++ dict(opt_str="--praptpl", callback=tcm_process_aptpl_metadata, nargs=1,
++ dest="HBA/DEV", help="Process PR APTPL metadata from file"),
++ dict(opt_str="--prshowmd", callback=tcm_show_aptpl_metadata, nargs=1,
++ dest="HBA/DEV", help="Show APTPL metadata file"),
++ dict(opt_str="--ramdisk", callback=tcm_create_ramdisk, nargs=2,
++ dest="HBA/DEV <PAGES>", help="Create and associate TCM/RAMDISK object"),
++ dict(opt_str=("--scsi","--pscsi"), callback=tcm_create_pscsi, nargs=2,
++ dest="HBA/DEV <C:T:L>",
++ help="Associate TCM/pSCSI object with Linux/SCSI device by bus location"),
++ dict(opt_str=("--scsibyudev", "--pscsibyudev"), callback=tcm_create_pscsibyudev, nargs=2,
++ dest="HBA/DEV <UDEV_PATH>",
++ help="Associate TCM/pSCSI object with Linux/SCSI device by UDEV Path"),
++ dict(opt_str="--setaluadelay", callback=tcm_set_alua_nonop_delay, nargs=3,
++ dest="HBA/DEV <TG_PT_GP_NAME> <NON_OP_DELAY_IN_MSECS>",
++ help="Set ALUA Target Port Group delay for Active/NonOptimized in milliseconds"),
++ dict(opt_str="--setaluapref", callback=tcm_set_alua_tgpt_pref, nargs=2,
++ dest="HBA/DEV <TG_PT_GP_NAME>", help="Set ALUA Target Port Group Preferred Bit"),
++ dict(opt_str="--setaluastate", callback=tcm_set_alua_state, nargs=3,
++ dest="HBA/DEV <TG_PT_GP_NAME> <ALUA_ACCESS_STATE>",
++ help="Set ALUA access state for TG_PT_GP_NAME on Storage Object. The value access"
++ " states are \"o\" = active/optimized, \"a\" = active/nonoptimized, \"s\" = standby,"
++ " \"u\" = unavailable"),
++ dict(opt_str="--setaluatransdelay", callback=tcm_set_alua_trans_delay, nargs=3,
++ dest="HBA/DEV <TG_PT_GP_NAME> <TRANS_DELAY_IN_MSECS>",
++ help="Set ALUA Target Port Group Transition delay"),
++ dict(opt_str="--setaluatype", callback=tcm_set_alua_type, nargs=3,
++ dest="HBA/DEV <TG_PT_GP_NAME> <ALUA_ACCESS_TYPE>",
++ help="Set ALUA access type for TG_PT_GP_NAME on Storage Object. The value type"
++ " states are \"both\" = implict/explict, \"explict\", \"implict\", or \"none\""),
++ dict(opt_str="--setdevattr", callback=tcm_set_dev_attrib, nargs=3,
++ dest="HBA/DEV <ATTRIB> <VALUE>",
++ help="Set new value for TCM storage object device attribute"),
++ dict(opt_str="--setlugp", callback=tcm_set_alua_lugp, nargs=2,
++ dest="HBA/DEV LU_GP_NAME", help="Set ALUA Logical Unit Group"),
++ dict(opt_str="--setudevpath", callback=tcm_set_udev_path, nargs=2,
++ dest="HBA/DEV <udev_path>",
++ help="Set UDEV Path Information, only used when --createdev did not contain"
++ " <udev_path> as parameter"),
++ dict(opt_str="--setunitserial", callback=tcm_set_wwn_unit_serial, nargs=2,
++ dest="HBA/DEV <unit_serial>", help="Set T10 EVPD Unit Serial Information"),
++ dict(opt_str="--setunitserialwithmd", callback=tcm_set_wwn_unit_serial_with_md, nargs=2,
++ dest="HBA/DEV <unit_serial>",
++ help="Set T10 EVPD Unit Serial Information and process PR APTPL metadata"),
++ dict(opt_str="--udevpath", callback=tcm_show_udev_path, nargs=1,
++ dest="HBA/DEV", help="Show UDEV Path Information for TCM storage object"),
++ dict(opt_str="--unload", callback=tcm_unload, nargs=0,
++ help="Unload target_core_mod"),
++ dict(opt_str="--wwn", callback=tcm_show_wwn_info, nargs=1,
++ dest="HBA/DEV", help="Show WWN info"),
++)
++
++def dispatcher(option, opt_str, value, parser, orig_callback):
++ if option.nargs == 1:
++ value = (value,)
++ value = [str(x).strip() for x in value]
++ orig_callback(*value)
++
++def main():
++
++ parser = OptionParser(version=tcm_version())
++
++ for opt in cmdline_options:
++ # cmd_aliases can be string or tuple of strings.
++ # we're unpacking below, so convert strings to 1 item tuples
++ cmd_aliases = opt["opt_str"]
++ if isinstance(cmd_aliases, basestring):
++ cmd_aliases = (cmd_aliases,)
++ del opt["opt_str"]
++ # common params for all options
++ opt["action"] = "callback"
++ opt["type"] = "string"
++ opt["callback_kwargs"] = dict(orig_callback=opt["callback"])
++ opt["callback"] = dispatcher
++ parser.add_option(*cmd_aliases, **opt)
++
++ (options, args) = parser.parse_args()
++ if len(sys.argv) == 1:
++ parser.print_help()
++ sys.exit(0)
++ elif not re.search('--', sys.argv[1]):
++ tcm_err("Unknown CLI option: " + sys.argv[1])
++
++if __name__ == "__main__":
++ main()
+diff --git a/targetcli/tcm_pscsi.py b/targetcli/tcm_pscsi.py
+new file mode 100644
+index 0000000..f849279
+--- /dev/null
++++ b/targetcli/tcm_pscsi.py
+@@ -0,0 +1,184 @@
++import os
++import subprocess as sub
++import string, re
++from optparse import OptionParser
++
++tcm_root = "/sys/kernel/config/target/core"
++
++def print_lsscsi(option, opt_str, value, parser):
++ command = "lsscsi"
++
++ p = sub.Popen(command,shell=True, stdout=sub.PIPE).stdout
++ while 1:
++ line = p.readline()
++ if not line: break
++ print line,
++
++def pscsi_get_hba_prefix(arg):
++ path = "/sys/kernel/config/target/core/pscsi_" + arg
++ return path
++
++def pscsi_scan_lsscsi(option, opt_str, value, parser):
++ command = "lsscsi -H"
++ p = sub.Popen(command,shell=True, stdout=sub.PIPE).stdout
++ while 1:
++ line = p.readline()
++ if not line: break
++ line.split()
++ host_id = line[1]
++ print "SCSI Host ID: " + host_id
++
++ cfs_path = pscsi_get_hba_prefix(host_id)
++ if (os.path.isdir(cfs_path)):
++ print "pSCSI HBA already registered, skipping"
++ continue
++
++ print cfs_path
++ ret = os.mkdir(cfs_path)
++ print "os.path.mkdir ret: " + str(ret)
++ if not ret:
++ print "Successfully added ConfigFS path " + cfs_path
++
++def createvirtdev(path, params):
++
++# print "Calling pscsi createvirtdev: path " + path
++ cfs_path = tcm_root + "/" + path + "/"
++
++# print "Calling pscsi createvirtdev: params " + str(params)
++ pscsi_params = params[0]
++# print pscsi_params
++
++ # Exract HCTL from sysfs and set udev_path
++ if re.search('/dev/', pscsi_params):
++ udev_path = pscsi_params.rstrip()
++ if re.search('/dev/disk/', udev_path):
++ udev_op = "/bin/ls -l " + udev_path
++ p = sub.Popen(udev_op, shell=True, stdout=sub.PIPE).stdout
++ if not p:
++ print "pSCSI: Unable to locate scsi_device from udev_path: " + udev_path
++ return -1
++
++ line = p.readline()
++ out = line.split(' ../../');
++ p.close()
++ if not out:
++ print "pSCSI: Unable to locate scsi_device from udev_path: " + udev_path
++ return -1
++
++ scsi_dev = out[1].rstrip()
++ elif re.search('/dev/s', udev_path):
++ out = udev_path.split('/dev/')
++ scsi_dev = out[1]
++ else:
++ print "pSCSI: Unable to locate scsi_device from udev_path: " + udev_path
++ return -1
++
++ # Convert scdX to sr0 for TYPE_ROM in /sys/block/
++ if re.search('scd', scsi_dev):
++ scsi_dev = scsi_dev.replace('scd', 'sr');
++
++ if not os.path.isdir("/sys/block/" + scsi_dev + "/device/"):
++ print "pSCSI: Unable to locate scsi_device from udev_path: " + udev_path
++ return -1
++
++ scsi_dev_sysfs = "/sys/block/" + scsi_dev + "/device"
++ udev_op = "/bin/ls -l " + scsi_dev_sysfs
++ p = sub.Popen(udev_op, shell=True, stdout=sub.PIPE).stdout
++ if not p:
++ print "pSCSI: Unable to locate scsi_device from udev_path: " + udev_path
++ return -1
++
++ line = p.readline()
++ out = line.split('/')
++ p.close()
++
++ scsi_hctl_tmp = out[len(out)-1]
++ scsi_hctl = scsi_hctl_tmp.split(':')
++ scsi_host_id = scsi_hctl[0]
++ scsi_channel_id = scsi_hctl[1]
++ scsi_target_id = scsi_hctl[2]
++ scsi_lun_id = scsi_hctl[3]
++ print "pSCSI: Referencing HCTL " + out[1].rstrip() + " for udev_path: " + udev_path
++
++ set_udev_path_op = "echo -n " + udev_path + " > " + cfs_path + "udev_path"
++ ret = os.system(set_udev_path_op)
++ if ret:
++ print "pSCSI: Unable to set udev_path in " + cfs_path + " for: " + udev_path
++ return -1
++
++ pscsi_params = "scsi_host_id=" + scsi_host_id + ",scsi_channel_id=" + scsi_channel_id + ",scsi_target_id=" + scsi_target_id + ",scsi_lun_id=" + scsi_lun_id.rstrip()
++
++
++ control_opt = "echo -n " + pscsi_params + " > " + cfs_path + "control"
++# print "Calling control_opt " + control_opt
++ ret = os.system(control_opt)
++ if ret:
++ print "pSCSI: createvirtdev failed for control_opt with " + pscsi_params
++ return -1
++
++ enable_opt = "echo 1 > " + cfs_path + "enable"
++# print "Calling enable_opt " + enable_opt
++ ret = os.system(enable_opt)
++ if ret:
++ print "pSCSI: createvirtdev failed for enable_opt with " + pscsi_params
++ return -1
++
++def pscsi_freevirtdev():
++ pass
++
++def pscsi_get_params(path):
++ # Reference by udev_path if available
++ udev_path_file = path + "/udev_path"
++ p = os.open(udev_path_file, 0)
++ value = os.read(p, 1024)
++ if re.search('/dev/', value):
++ os.close(p)
++ return value.rstrip()
++
++ os.close(p)
++
++ info_file = path + "/info"
++ p = open(info_file, 'rU')
++ try:
++ value = p.read(1024)
++ except IOError, msg:
++ p.close()
++ return
++ p.close()
++
++ off = value.index('Channel ID: ')
++ off += 12
++ channel_id_tmp = value[off:]
++ channel_id = channel_id_tmp.split(' ')
++ off = value.index('Target ID: ')
++ off += 11
++ target_id_tmp = value[off:]
++ target_id = target_id_tmp.split(' ')
++ off = value.index('LUN: ')
++ off += 5
++ lun_id_tmp = value[off:]
++ lun_id = lun_id_tmp.split(' ')
++ params = ""
++
++ try:
++ off = value.index('Host ID: ')
++ except ValueError:
++ params = ""
++ else:
++ off += 9
++ host_id_tmp = value[off:]
++ host_id = host_id_tmp.split(' ')
++ host_id = host_id[0].rstrip()
++ if host_id != "PHBA":
++ params += "scsi_host_id=" + host_id[0] + ","
++
++ params += "scsi_channel_id=" + channel_id[0] + ",scsi_target_id=" + target_id[0] + ",scsi_lun_id=" + lun_id[0].rstrip()
++
++ # scsi_channel_id=, scsi_target_id= and scsi_lun_id= reference for tcm_node --createdev
++ return params
++
++#parser = OptionParser()
++#parser.add_option("-s", "--scan", action="callback", callback=pscsi_scan_lsscsi,
++# default=False, help="Scan and register pSCSI HBAs with TCM/ConfigFS")
++#parser.parse_args()
++#
+diff --git a/targetcli/tcm_ramdisk.py b/targetcli/tcm_ramdisk.py
+new file mode 100644
+index 0000000..10b4a5f
+--- /dev/null
++++ b/targetcli/tcm_ramdisk.py
+@@ -0,0 +1,53 @@
++import os
++import subprocess as sub
++import string, re
++from optparse import OptionParser
++
++tcm_root = "/sys/kernel/config/target/core"
++
++def createvirtdev(path, params):
++
++# print "Calling ramdisk createvirtdev: path " + path
++ cfs_path = tcm_root + "/" + path + "/"
++# print "Calling ramdisk createvirtdev: params " + str(params)
++ rd_pages = params[0]
++
++ rd_params = "rd_pages=" + rd_pages
++# print "rd_params: " + rd_params
++
++ control_opt = "echo -n " + rd_params.rstrip() + " > " + cfs_path + "/control"
++# print "control_opt: " + control_opt
++ ret = os.system(control_opt)
++ if ret:
++ print "RAMDISK: createvirtdev failed for control_opt with " + rd_params
++ return -1
++
++ enable_opt = "echo 1 > " + cfs_path + "enable"
++# print "Calling enable_opt " + enable_opt
++ ret = os.system(enable_opt)
++ if ret:
++ print "RAMDISK: createvirtdev failed for enable_opt with " + rd_params
++ return -1
++
++def rd_freevirtdev():
++ pass
++
++def rd_get_params(path):
++
++ info_file = path + "/info"
++ p = open(info_file, 'rU')
++ try:
++ value = p.read(1024)
++ except IOError, msg:
++ p.close()
++ return
++ p.close()
++
++ off = value.index('PAGE_SIZE: ')
++ off += 11 # Skip over "PAGE_SIZE: "
++ rd_pages_tmp = value[off:]
++ rd_pages = rd_pages_tmp.split('*')
++ params = "rd_pages=" + rd_pages[0]
++
++ # rd_pages= parameter for tcm_node --createdev
++ return rd_pages[0]
+--
+1.7.1
+
diff --git a/0003-Hack.-dump-scripts-aren-t-in-PATH-anymore-so-call-th.patch b/0003-Hack.-dump-scripts-aren-t-in-PATH-anymore-so-call-th.patch
new file mode 100644
index 0000000..39dc87e
--- /dev/null
+++ b/0003-Hack.-dump-scripts-aren-t-in-PATH-anymore-so-call-th.patch
@@ -0,0 +1,136 @@
+From 7b4dce12237dc9b79dbe4f2ac9dbbb125d314b2c Mon Sep 17 00:00:00 2001
+From: Andy Grover <agrover at redhat.com>
+Date: Sat, 30 Jul 2011 18:31:45 -0700
+Subject: [PATCH 3/6] Hack. dump scripts aren't in PATH anymore, so call them explicitly.
+
+Signed-off-by: Andy Grover <agrover at redhat.com>
+---
+ targetcli/lio_dump.py | 3 ++-
+ targetcli/tcm_dump.py | 22 +++++++++++++---------
+ targetcli/tcm_fabric.py | 4 ++--
+ 3 files changed, 17 insertions(+), 12 deletions(-)
+
+diff --git a/targetcli/lio_dump.py b/targetcli/lio_dump.py
+index 81c5104..6ce217a 100644
+--- a/targetcli/lio_dump.py
++++ b/targetcli/lio_dump.py
+@@ -221,7 +221,8 @@ def lio_backup_to_file(option, opt_str, value, parser):
+ print "Unable to open backup_dir"
+ sys.exit(1)
+
+- op = "lio_dump --stdout"
++ prefix = "python /usr/lib/python2.6/site-packages/rtsadmin/"
++ op = prefix + "lio_dump.py --stdout"
+ p = sub.Popen(op, shell=True, stdout=sub.PIPE).stdout
+ if not p:
+ print "Unable to dump LIO-Target/ConfigFS running state"
+diff --git a/targetcli/tcm_dump.py b/targetcli/tcm_dump.py
+index bf80632..1b76b62 100644
+--- a/targetcli/tcm_dump.py
++++ b/targetcli/tcm_dump.py
+@@ -20,6 +20,10 @@ tcm_root = "/sys/kernel/config/target/core"
+ def tcm_dump_hba_devices():
+ pass
+
++path_prefix = "python /usr/lib/python2.6/site-packages/rtsadmin/"
++tcm_node_path = path_prefix + "tcm_node.py"
++tcm_dump_path = path_prefix + "tcm_dump.py"
++
+ def tcm_dump_configfs(option, opt_str, value, parser):
+
+ if not os.path.isdir(tcm_root):
+@@ -68,35 +72,35 @@ def tcm_dump_configfs(option, opt_str, value, parser):
+ params = tcm_pscsi.pscsi_get_params(dev)
+ if not params:
+ continue
+- print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ print tcm_node_path + " --establishdev " + f + "/" + g + " " + str(params)
+ result = re.search('iblock_', f)
+ if result:
+ dev = dev_root + g
+ params = tcm_iblock.iblock_get_params(dev)
+ if not params:
+ continue
+- print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ print tcm_node_path + " --establishdev " + f + "/" + g + " " + str(params)
+ result = re.search('rd_dr_', f)
+ if result:
+ dev = dev_root + g
+ params = tcm_ramdisk.rd_get_params(dev)
+ if not params:
+ continue
+- print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ print tcm_node_path + " --establishdev " + f + "/" + g + " " + str(params)
+ result = re.search('rd_mcp_', f)
+ if result:
+ dev = dev_root + g
+ params = tcm_ramdisk.rd_get_params(dev)
+ if not params:
+ continue
+- print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ print tcm_node_path + " --establishdev " + f + "/" + g + " " + str(params)
+ result = re.search('fileio_', f)
+ if result:
+ dev = dev_root + g
+ params = tcm_fileio.fd_get_params(dev)
+ if not params:
+ continue
+- print "tcm_node --establishdev " + f + "/" + g + " " + str(params)
++ print tcm_node_path + " --establishdev " + f + "/" + g + " " + str(params)
+
+ # Dump T10 VP Unit Serial for all non Target_Core_Mod/pSCSI objects
+ result = re.search('pscsi_', f)
+@@ -108,7 +112,7 @@ def tcm_dump_configfs(option, opt_str, value, parser):
+ off += 8 # Skip over "Number: "
+ unit_serial = value[off:]
+ # Note that this will handle read, parse and set any PR APTPL metadata
+- print "tcm_node --setunitserialwithmd " + f + "/" + g + " " + unit_serial.rstrip()
++ print tcm_node_path + " --setunitserialwithmd " + f + "/" + g + " " + unit_serial.rstrip()
+ os.close(p)
+
+ # Dump device object alias
+@@ -146,7 +150,7 @@ def tcm_dump_configfs(option, opt_str, value, parser):
+ os.close(p)
+ if not value:
+ continue
+- print "tcm_node --addaluatpgwithmd " + f + "/" + g + " " + tg_pt_gp + " " + value.rstrip()
++ print tcm_node_path + " --addaluatpgwithmd " + f + "/" + g + " " + tg_pt_gp + " " + value.rstrip()
+ # Dump the ALUA types
+ tg_pt_gp_type_file = dev_root + g + "/alua/" + tg_pt_gp + "/alua_access_type"
+ p = os.open(tg_pt_gp_type_file, 0)
+@@ -244,7 +248,7 @@ def tcm_dump_configfs(option, opt_str, value, parser):
+ print "echo " + attr_val + " > " + attrib_file
+
+ if snap_enabled == 1:
+- print "tcm_node --lvsnapstart " + f + "/" + g
++ print tcm_node_path + " --lvsnapstart " + f + "/" + g
+
+ def tcm_backup_to_file(option, opt_str, value, parser):
+ datetime = str(value)
+@@ -261,7 +265,7 @@ def tcm_backup_to_file(option, opt_str, value, parser):
+ print "Unable to open backup_dir"
+ sys.exit(1)
+
+- op = "tcm_dump --stdout"
++ op = tcm_dump_path + " --stdout"
+ p = sub.Popen(op, shell=True, stdout=sub.PIPE).stdout
+ if not p:
+ print "Unable to dump Target_Core_Mod/ConfigFS running state"
+diff --git a/targetcli/tcm_fabric.py b/targetcli/tcm_fabric.py
+index 8a843d4..03f5570 100644
+--- a/targetcli/tcm_fabric.py
++++ b/targetcli/tcm_fabric.py
+@@ -254,8 +254,8 @@ def fabric_backup_to_file(date_time, fabric_name, fabric_root, module_name):
+ if ret:
+ print "Unable to open backup_dir"
+ sys.exit(1)
+-
+- op = "tcm_fabric --stdout --fabric-name=" + fabric_name + " --fabric-root=" + fabric_root + " --module-name=" + module_name
++ prefix = "python /usr/lib/python2.6/site-packages/rtsadmin/"
++ op = prefix + "tcm_fabric.py --stdout --fabric-name=" + fabric_name + " --fabric-root=" + fabric_root + " --module-name=" + module_name
+ # print "Using op: " + op
+ p = sub.Popen(op, shell=True, stdout=sub.PIPE).stdout
+ if not p:
+--
+1.7.1
+
diff --git a/0004-ignore-errors-from-failure-to-set-device-attributes.patch b/0004-ignore-errors-from-failure-to-set-device-attributes.patch
new file mode 100644
index 0000000..5a409ee
--- /dev/null
+++ b/0004-ignore-errors-from-failure-to-set-device-attributes.patch
@@ -0,0 +1,26 @@
+From 8f6fb804f7bf68bafa4799fb821882bd97e777c8 Mon Sep 17 00:00:00 2001
+From: Andy Grover <agrover at redhat.com>
+Date: Mon, 1 Aug 2011 14:40:12 -0700
+Subject: [PATCH 4/6] ignore errors from failure to set device attributes
+
+Signed-off-by: Andy Grover <agrover at redhat.com>
+---
+ targetcli/tcm_dump.py | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/targetcli/tcm_dump.py b/targetcli/tcm_dump.py
+index 1b76b62..abfd592 100644
+--- a/targetcli/tcm_dump.py
++++ b/targetcli/tcm_dump.py
+@@ -211,7 +211,7 @@ def tcm_dump_configfs(option, opt_str, value, parser):
+ attrib_file = dev_attrib_root + h
+ p = os.open(attrib_file, 0)
+ value = os.read(p, 8)
+- print "echo " + value.rstrip() + " > " + attrib_file
++ print "echo " + value.rstrip() + " > " + attrib_file + " 2>/dev/null"
+ os.close(p)
+
+ # Dump snapshot attributes
+--
+1.7.1
+
diff --git a/0005-fix-spec_root-path.patch b/0005-fix-spec_root-path.patch
new file mode 100644
index 0000000..0ae75fa
--- /dev/null
+++ b/0005-fix-spec_root-path.patch
@@ -0,0 +1,29 @@
+From 8f91eb2b471f8db02888e80d37f90b1c6c42605a Mon Sep 17 00:00:00 2001
+From: Andy Grover <agrover at redhat.com>
+Date: Thu, 25 Aug 2011 14:41:29 -0700
+Subject: [PATCH 5/6] fix spec_root path
+
+We moved the specs from /var/target/fabric to /var/lib/target/fabric, so
+this needs to be updated so saveconfig will work.
+
+Signed-off-by: Andy Grover <agrover at redhat.com>
+---
+ targetcli/tcm_fabric.py | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/targetcli/tcm_fabric.py b/targetcli/tcm_fabric.py
+index 03f5570..2c4c6dc 100644
+--- a/targetcli/tcm_fabric.py
++++ b/targetcli/tcm_fabric.py
+@@ -6,7 +6,7 @@ import datetime, time
+ import optparse
+
+ target_root = "/sys/kernel/config/target/"
+-spec_root = "/var/target/fabric/"
++spec_root = "/var/lib/target/fabric/"
+
+ def fabric_configfs_dump(fabric_name, fabric_root, module_name):
+
+--
+1.7.1
+
diff --git a/0006-add-docs.patch b/0006-add-docs.patch
new file mode 100644
index 0000000..0f556af
--- /dev/null
+++ b/0006-add-docs.patch
@@ -0,0 +1,174 @@
+From 705561247e8397dd5c1c33269ad6f7c79c639713 Mon Sep 17 00:00:00 2001
+From: Andy Grover <agrover at redhat.com>
+Date: Tue, 20 Sep 2011 11:27:59 -0700
+Subject: [PATCH 6/6] add docs
+
+add a man page.
+
+Signed-off-by: Andy Grover <agrover at redhat.com>
+---
+ targetcli.8 | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 files changed, 152 insertions(+), 0 deletions(-)
+ create mode 100644 targetcli.8
+
+diff --git a/targetcli.8 b/targetcli.8
+new file mode 100644
+index 0000000..d216ca0
+--- /dev/null
++++ b/targetcli.8
+@@ -0,0 +1,152 @@
++.TH targetcli 8
++.SH NAME
++.B targetcli
++.SH DESCRIPTION
++.B targetcli
++is a shell for viewing, editing, and saving the configuration of
++the kernel's target subsystem, also known as TCM/LIO. It enables the
++administrator to assign local storage resources backed by either files,
++volumes, or local SCSI devices, and export them to remote systems via
++network fabrics, such as iSCSI, FCoE, or others.
++.P
++The configuration layout is tree-based, similar to a filesystem, and
++navigated in a similar manner.
++.SH USAGE
++Invoke
++.B targetcli
++as root to enter the configuration shell. Use
++.B ls
++to list nodes below the current path. Configuration changes are made
++immediately. To retain changes on reboot, use
++.BR saveconfig .
++Moving
++around the tree is accomplished by the
++.B cd
++command, or by entering
++the new location directly. Use
++.B "help <cmd>"
++for additional usage
++information. Tab-completion is enabled for commands and command
++arguments.
++.SH EXAMPLES
++To export a storage resource, 1) define a storage object using
++backstore, then 2) export the object via the FCoE network fabric.
++.SS DEFINING A STORAGE OBJECT WITH BACKSTORE
++.B backstores/fileio0 create disk1 /disks/disk1.img 140M
++.br
++Creates a storage object named
++.I disk1
++with the given path and size.
++.B targetcli
++supports common size abbreviations like 'M', 'G', and 'T'.
++.P
++In addition to the
++.I fileio
++backstore for file-backed volumes, other backstore types include
++.I iblock
++for block-device-backed volumes, and
++.I pscsi
++for volumes backed by local SCSI devices. Please see the built-in help
++for more details on required parameters.
++.SS EXPORTING A STORAGE OBJECT VIA FCOE
++.B tcm_fc/ create 20:00:00:19:99:a8:34:bc
++.br
++Create an FCoE target with the given WWN.
++.B targetcli
++can tab-complete the WWN based on registered FCoE interfaces. If none
++are found, verify that they are properly configured and are shown in
++the output of
++.BR "fcoeadm -i" .
++.P
++.B tcm_fc/20:00:00:19:99:a8:34:bc/
++.br
++If
++.B auto_cd_after_create
++is set to false, change to the configuration node for the given
++target, equivalent to giving the command prefixed by
++.BR cd .
++.P
++.B luns/ create /backstores/fileio/disk1
++.br
++Create a new LUN for the interface, attached to a previously defined
++storage object. The storage object now shows up under the /backstores
++configuration node as
++.BR activated .
++.P
++.B acls/ create 00:99:88:77:66:55:44:33
++.br
++Create an ACL (access control list), for defining the resources each
++initiator may access. The default behavior is to auto-map existing
++LUNs to the ACL; please see help for more information.
++.P
++The LUN should now be accessible via FCoE.
++.ig
++.SS EXPORTING A STORAGE OBJECT VIA ISCSI
++.B iscsi/ create
++.br
++Creates an iSCSI target with a default WWN. It will also create an
++initial target portal group called
++.IR tpgt1 .
++.P
++.B iqn.2003-01.org.linux-iscsi.test2.x8664:sn123456789012/tpgt1/
++.br
++An example of changing to the configuration node for the given
++target's first target portal group (TPG). This is equivalent to giving
++the command prefixed by "cd". (Although more can be useful for certain
++setups, most configurations have a single TPG per target. In this
++case, configuring the TPG is equivalent to configuring the overall
++target.)
++.P
++.B portals/ create
++.br
++Add a portal, i.e. an address and TCP port via which the target can be
++contacted by initiators. Sane defaults are used if these are not
++specified.
++.P
++.B luns/ create /backstores/fileio0/disk1
++.br
++Create a new LUN in the TPG, attached to the storage object that has
++previously been defined. The storage object now shows up under the
++/backstores configuration node as activated.
++.P
++.B acls/ create iqn.1994-05.com.redhat:4321576890
++.br
++Creates an ACL (access control list) for the given iSCSI initiator.
++.P
++.B acls/iqn.1994-05.com.redhat:4321576890 create 2 0
++.br
++Gives the initiator access to the first exported LUN (lun0), which the
++initiator will see as lun2. The default is to give the initiator
++read/write access; if read-only access was desired, an additional "1"
++argument would be added to enable write-protect.
++..
++.SS OTHER COMMANDS
++.B saveconfig
++.br
++Save the current configuration settings to a file, from which
++settings will be restored if the system is rebooted.
++.P
++This command must be executed from the configuration root node.
++.P
++.B exit
++.br
++Leave the configuration shell.
++.SS SETTINGS
++.B set attribute authentication=0
++.br
++Disable CHAP authentication.
++.P
++.B set global auto_cd_after_create=false
++.br
++Do not change into the configuration node of a newly created
++object. The default is 'true'.
++.SH FILES
++.B /etc/target/*
++.br
++.B /var/lib/target/*
++.SH AUTHOR
++Written by Jerome Martin <jxm at risingtidesystems.com>.
++.br
++Man page written by Andy Grover <agrover at redhat.com>.
++.SH REPORTING BUGS
++Report bugs to <target-devel at vger.kernel.org>
+--
+1.7.1
+
diff --git a/0007-all-start.patch b/0007-all-start.patch
new file mode 100644
index 0000000..477572a
--- /dev/null
+++ b/0007-all-start.patch
@@ -0,0 +1,35 @@
+commit 1af9a2ce6f1ebb15a43439c7c438b59d69568fd5
+Author: Andy Grover <agrover at redhat.com>
+Date: Wed Nov 2 14:46:39 2011 -0700
+
+ append all start scripts into /etc/target/all_start.sh
+
+ This should make systemd packaging a little easier.
+
+ Signed-off-by: Andy Grover <agrover at redhat.com>
+
+diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py
+index 2048544..b3f78c4 100644
+--- a/targetcli/ui_root.py
++++ b/targetcli/ui_root.py
+@@ -89,6 +89,20 @@ class UIRoot(UINode):
+ else:
+ self.shell.log.warning("Aborted, configuration left untouched.")
+
++ # append all into single script
++ from glob import iglob
++ import os
++ import shutil
++ sources = iglob(os.path.join("/etc/target", '*_start.sh'))
++ # ensure tcm_start is appended first
++ sources = [x for x in sources if not 'tcm_' in x]
++ sources = [x for x in sources if not 'all_' in x]
++ sources.insert(0, '/etc/target/tcm_start.sh')
++ dest = open('/etc/target/all_start.sh', 'wb')
++ for filename in sources:
++ shutil.copyfileobj(open(filename, 'rb'), dest)
++ dest.close()
++
+ def ui_command_version(self):
+ '''
+ Displays the targetcli and support libraries versions.
diff --git a/sources b/sources
index e69de29..049663c 100644
--- a/sources
+++ b/sources
@@ -0,0 +1 @@
+d90375b632ffb27700fa006b0e39e3f6 targetcli-1.99.2.gitb03ec79.tar.gz
diff --git a/targetcli-git-version.patch b/targetcli-git-version.patch
new file mode 100644
index 0000000..66ed45d
--- /dev/null
+++ b/targetcli-git-version.patch
@@ -0,0 +1,13 @@
+diff --git a/rtsadmin/__init__.py b/rtsadmin/__init__.py
+index 90150a8..c8a8f07 100644
+--- a/targetcli/__init__.py
++++ b/targetcli/__init__.py
+@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ from ui_root import UIRoot
+
+-__version__ = 'GIT_VERSION'
++__version__ = 'b03ec79'
+ __author__ = "Jerome Martin <jxm at risingtidesystems.com>"
+ __url__ = "http://www.risingtidesystems.com"
+ __description__ = "An administration shell for RTS storage targets."
diff --git a/targetcli.service b/targetcli.service
new file mode 100644
index 0000000..1108bfc
--- /dev/null
+++ b/targetcli.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Loads LIO kernel target saved configuration
+Requires=sys-kernel-config.mount
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=-/bin/sh /etc/target/all_start.sh
+SyslogIdentifier=targetcli
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/targetcli.spec b/targetcli.spec
new file mode 100644
index 0000000..81e7b9a
--- /dev/null
+++ b/targetcli.spec
@@ -0,0 +1,92 @@
+Name: targetcli
+License: AGPLv3
+Group: System Environment/Libraries
+Summary: An administration shell for storage targets
+Version: 1.99.2.gitb03ec79
+Release: 3%{?dist}
+# placeholder URL and source entries
+# archive created using:
+# git clone git://risingtidesystems.com/targetcli.git
+# cd targetcli
+# git archive b03ec79 --prefix targetcli-%{version}/ | gzip > targetcli-%{version}.tar.gz
+URL: http://www.risingtidesystems.com/git/
+Source: %{name}-%{version}.tar.gz
+Source1: targetcli.service
+Patch1: targetcli-git-version.patch
+Patch2: 0001-Remove-ads-from-cli-welcome-msg.-Mention-help-is-ava.patch
+Patch3: 0002-bundle-lio-utils.patch
+Patch4: 0003-Hack.-dump-scripts-aren-t-in-PATH-anymore-so-call-th.patch
+Patch5: 0004-ignore-errors-from-failure-to-set-device-attributes.patch
+Patch6: 0005-fix-spec_root-path.patch
+Patch7: 0006-add-docs.patch
+Patch8: 0007-all-start.patch
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildArch: noarch
+BuildRequires: python-devel python-rtslib python-configshell epydoc
+BuildRequires: systemd-units
+Requires: python-rtslib python-configshell
+Requires(post): systemd-units
+
+
+%description
+An administration shell for configuring iSCSI, FCoE, and other
+SCSI targets, using the TCM/LIO kernel target subsystem. FCoE
+users will also need to install and use fcoe-utils.
+
+
+%prep
+%setup -q -n %{name}-%{version}
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+
+%build
+%{__python} setup.py build
+gzip --stdout targetcli.8 > targetcli.8.gz
+
+%install
+rm -rf %{buildroot}
+%{__python} setup.py install --skip-build --root %{buildroot}
+mkdir -p %{buildroot}%{_sysconfdir}/rc.d/init.d
+mkdir -p %{buildroot}%{_sysconfdir}/target/backup
+mkdir -p %{buildroot}%{_mandir}/man8/
+mkdir -p %{buildroot}%{_unitdir}
+install -m 644 %{SOURCE1} %{buildroot}%{_unitdir}/targetcli.service
+install -m 644 targetcli.8.gz %{buildroot}%{_mandir}/man8/
+
+%clean
+rm -rf %{buildroot}
+
+%post
+if [ $1 -eq 1 ] ; then
+ # Initial installation
+ /bin/systemctl enable targetcli.service >/dev/null 2>&1 || :
+fi
+
+%files
+%defattr(-,root,root,-)
+%{python_sitelib}
+%{_bindir}/targetcli
+%{_unitdir}/targetcli.service
+%dir %{_sysconfdir}/target/backup
+%doc COPYING README
+%{_mandir}/man8/targetcli.8.gz
+
+%changelog
+* Wed Nov 2 2011 Andy Grover <agrover at redhat.com> - 1.99.2.gitb03ec79-3
+- Add buildrequires for systemd-units
+- use _unitdir
+- remove preun, modify post
+
+* Wed Nov 2 2011 Andy Grover <agrover at redhat.com> - 1.99.2.gitb03ec79-2
+- Add patch
+ * 0007-all-start.patch
+- Replace sysv init with systemd init
+
+* Fri Oct 7 2011 Andy Grover <agrover at redhat.com> - 1.99.2.gitb03ec79-1
+- Initial Fedora packaging
More information about the scm-commits
mailing list