From c55d9206e4f783d25ceb574ba07c40a83daf00a3 Mon Sep 17 00:00:00 2001 From: Andy Grover 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 --- 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-.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 ", 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 ", 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 ", 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-.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-.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 ", + 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 ", + 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 ", + help="Associate TCM/IBLOCK object with Linux/BLOCK device"), + dict(opt_str="--clearaluapref", callback=tcm_clear_alua_tgpt_pref, + nargs=2, dest="HBA/DEV ", + 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 ", + 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 ", + 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 ", + 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 ", + 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 ", help="Create and associate TCM/RAMDISK object"), + dict(opt_str=("--scsi","--pscsi"), callback=tcm_create_pscsi, nargs=2, + dest="HBA/DEV ", + 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 ", + 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 ", + 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 ", help="Set ALUA Target Port Group Preferred Bit"), + dict(opt_str="--setaluastate", callback=tcm_set_alua_state, nargs=3, + dest="HBA/DEV ", + 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 ", + help="Set ALUA Target Port Group Transition delay"), + dict(opt_str="--setaluatype", callback=tcm_set_alua_type, nargs=3, + dest="HBA/DEV ", + 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 ", + 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 ", + help="Set UDEV Path Information, only used when --createdev did not contain" + " as parameter"), + dict(opt_str="--setunitserial", callback=tcm_set_wwn_unit_serial, nargs=2, + dest="HBA/DEV ", help="Set T10 EVPD Unit Serial Information"), + dict(opt_str="--setunitserialwithmd", callback=tcm_set_wwn_unit_serial_with_md, nargs=2, + dest="HBA/DEV ", + 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