[targetcli] Initial import (#744340)

Andy Grover grover at fedoraproject.org
Fri Nov 4 22:18:13 UTC 2011


commit 2b97aa1fee6414e0be23a4e45efc8d4e2487f12a
Author: Andy Grover <agrover at redhat.com>
Date:   Fri Nov 4 15:17:44 2011 -0700

    Initial import (#744340)

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


More information about the scm-commits mailing list