From eb47f5caa04cbe2bfe64f238e96d63d9453ea4fc Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 14 2018 13:02:29 +0000 Subject: import targetcli-2.1.fb46-4.el7_5 --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..849e799 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/targetcli-fb-2.1.fb46.tar.gz diff --git a/.targetcli.metadata b/.targetcli.metadata new file mode 100644 index 0000000..8fae95d --- /dev/null +++ b/.targetcli.metadata @@ -0,0 +1 @@ +cbfc05ef7baac08165fdb17efe26081eb9850363 SOURCES/targetcli-fb-2.1.fb46.tar.gz diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/0001-Properly-detect-errors-when-writing-backup-files.-Cl.patch b/SOURCES/0001-Properly-detect-errors-when-writing-backup-files.-Cl.patch new file mode 100644 index 0000000..ecefe7e --- /dev/null +++ b/SOURCES/0001-Properly-detect-errors-when-writing-backup-files.-Cl.patch @@ -0,0 +1,72 @@ +From 8011ea6a741d494c145b4906f7a7865c8b74c6a7 Mon Sep 17 00:00:00 2001 +From: Christian Seiler +Date: Thu, 23 Mar 2017 19:36:18 +0100 +Subject: [PATCH] Properly detect errors when writing backup files. (Closes: + #80) (#81) + +* Properly detect errors when writing backup files. (Closes: #80) + +If the backup directory does not exist, properly detect that and show a +warning message to the user, so that they don't think that their +configuration was backed up, when it fact wasn't. + +Additionally, try to automatically create the backup directory if it +does not exist. + +Signed-off-by: Christian Seiler + +* Don't automatically create backup directory if it doesn't exist. + +After discussion on the issue tracker, it was decided to not +auto-create the backup-directory if it doesn't exist yet. + +If the directory doesn't exist, the user will now see the warning. + +Signed-off-by: Christian Seiler +--- + targetcli/ui_root.py | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py +index 2d8989c..c3a8483 100644 +--- a/targetcli/ui_root.py ++++ b/targetcli/ui_root.py +@@ -75,17 +75,25 @@ class UIRoot(UINode): + backup_name = "saveconfig-" + \ + datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json" + backupfile = backup_dir + "/" + backup_name +- with ignored(IOError): ++ backup_error = None ++ try: + shutil.copy(savefile, backupfile) +- +- # Kill excess backups +- backups = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) +- files_to_unlink = list(reversed(backups))[kept_backups:] +- for f in files_to_unlink: +- os.unlink(f) +- +- self.shell.log.info("Last %d configs saved in %s." % \ +- (kept_backups, backup_dir)) ++ except IOError as ioe: ++ backup_error = ioe.strerror or "Unknown error" ++ ++ if backup_error == None: ++ # Kill excess backups ++ backups = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) ++ files_to_unlink = list(reversed(backups))[kept_backups:] ++ for f in files_to_unlink: ++ with ignored(IOError): ++ os.unlink(f) ++ ++ self.shell.log.info("Last %d configs saved in %s." % \ ++ (kept_backups, backup_dir)) ++ else: ++ self.shell.log.warning("Could not create backup file %s: %s." % \ ++ (backupfile, backup_error)) + + self.rtsroot.save_to_file(savefile) + +-- +1.8.3.1 + diff --git a/SOURCES/0002-Read-number-of-backup-files-to-keep-from-file.patch b/SOURCES/0002-Read-number-of-backup-files-to-keep-from-file.patch new file mode 100644 index 0000000..6c591e3 --- /dev/null +++ b/SOURCES/0002-Read-number-of-backup-files-to-keep-from-file.patch @@ -0,0 +1,52 @@ +From bf0fb387cef89bb0dc51cf1740ab73abf549c38e Mon Sep 17 00:00:00 2001 +From: Taylor Jakobson +Date: Mon, 23 Oct 2017 14:26:03 -0500 +Subject: [PATCH] Read number of backup files to keep from file + +Use /etc/target/targetcli.conf file to store universal configurations +that are the same for all users. +Configuration file has format of: + key = value +--- + targetcli/ui_root.py | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py +index c3a8483..8bc8521 100644 +--- a/targetcli/ui_root.py ++++ b/targetcli/ui_root.py +@@ -20,6 +20,7 @@ under the License. + from datetime import datetime + from glob import glob + import os ++import re + import shutil + import stat + +@@ -32,7 +33,8 @@ from .ui_node import UINode + from .ui_target import UIFabricModule + + default_save_file = "/etc/target/saveconfig.json" +-kept_backups = 10 ++universal_prefs_file = "/etc/target/targetcli.conf" ++default_kept_backups = 10 + + class UIRoot(UINode): + ''' +@@ -83,6 +85,13 @@ class UIRoot(UINode): + + if backup_error == None: + # Kill excess backups ++ try: ++ with open(universal_prefs_file) as prefs: ++ backups = [line for line in prefs.read().splitlines() if re.match('^kept_backups\s*=', line)] ++ kept_backups = int(backups[0].split('=')[1].strip()) ++ except: ++ kept_backups = default_kept_backups ++ + backups = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) + files_to_unlink = list(reversed(backups))[kept_backups:] + for f in files_to_unlink: +-- +1.8.3.1 + diff --git a/SOURCES/0003-config-defend-on-etc-target-backup-directory.patch b/SOURCES/0003-config-defend-on-etc-target-backup-directory.patch new file mode 100644 index 0000000..837f9af --- /dev/null +++ b/SOURCES/0003-config-defend-on-etc-target-backup-directory.patch @@ -0,0 +1,99 @@ +From 6349a75bd71f2f15c3acd89588321524c94676e8 Mon Sep 17 00:00:00 2001 +From: Prasanna Kumar Kalever +Date: Fri, 15 Dec 2017 15:16:28 +0530 +Subject: [PATCH] config: defend on '/etc/target/backup' directory + +Currently we do not create '/etc/target/backup/' while we expect the directory +to be presented by package (rpm). + +If for some reason '/etc/target/backup' is not available, say may be a +unintentional deletion or use of targetcli which is compiled from source, +we see below errors on up on saveconfig command, + +Case 1: No '/etc/target/backup/' dir + +$ targetcli / saveconfig +Could not create backup file /etc/target/backup/saveconfig-20171215-15:26:48.json: No such file or directory. +Configuration saved to /etc/target/saveconfig.json + +Case 2: No '/etc/target/' dir + +$ targetcli / saveconfig +Could not create backup file /etc/target/backup/saveconfig-20171215-15:27:42.json: No such file or directory. +[Errno 2] No such file or directory: '/etc/target/saveconfig.json.temp' + +This patch tries to create '/etc/target/backup' directory tree in case if it +is absent. + +Signed-off-by: Prasanna Kumar Kalever +--- + targetcli/ui_root.py | 50 +++++++++++++++++++++++++++++--------------------- + 1 file changed, 29 insertions(+), 21 deletions(-) + +diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py +index 8bc8521..33bb948 100644 +--- a/targetcli/ui_root.py ++++ b/targetcli/ui_root.py +@@ -78,30 +78,38 @@ class UIRoot(UINode): + datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json" + backupfile = backup_dir + "/" + backup_name + backup_error = None +- try: +- shutil.copy(savefile, backupfile) +- except IOError as ioe: +- backup_error = ioe.strerror or "Unknown error" + +- if backup_error == None: +- # Kill excess backups ++ if not os.path.exists(backup_dir): ++ try: ++ os.makedirs(backup_dir); ++ except OSError as exe: ++ raise ExecutionError("Cannot create backup directory [%s] %s." % (backup_dir, exc.strerror)) ++ ++ if os.path.exists(savefile): + try: +- with open(universal_prefs_file) as prefs: +- backups = [line for line in prefs.read().splitlines() if re.match('^kept_backups\s*=', line)] +- kept_backups = int(backups[0].split('=')[1].strip()) +- except: +- kept_backups = default_kept_backups +- +- backups = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) +- files_to_unlink = list(reversed(backups))[kept_backups:] +- for f in files_to_unlink: +- with ignored(IOError): +- os.unlink(f) +- +- self.shell.log.info("Last %d configs saved in %s." % \ ++ shutil.copy(savefile, backupfile) ++ except IOError as ioe: ++ backup_error = ioe.strerror or "Unknown error" ++ ++ if backup_error == None: ++ # Kill excess backups ++ try: ++ with open(universal_prefs_file) as prefs: ++ backups = [line for line in prefs.read().splitlines() if re.match('^kept_backups\s*=', line)] ++ kept_backups = int(backups[0].split('=')[1].strip()) ++ except: ++ kept_backups = default_kept_backups ++ ++ backups = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) ++ files_to_unlink = list(reversed(backups))[kept_backups:] ++ for f in files_to_unlink: ++ with ignored(IOError): ++ os.unlink(f) ++ ++ self.shell.log.info("Last %d configs saved in %s." % \ + (kept_backups, backup_dir)) +- else: +- self.shell.log.warning("Could not create backup file %s: %s." % \ ++ else: ++ self.shell.log.warning("Could not create backup file %s: %s." % \ + (backupfile, backup_error)) + + self.rtsroot.save_to_file(savefile) +-- +1.8.3.1 + diff --git a/SOURCES/0004-config-backup-when-current-config-is-different-from-.patch b/SOURCES/0004-config-backup-when-current-config-is-different-from-.patch new file mode 100644 index 0000000..69ed6cc --- /dev/null +++ b/SOURCES/0004-config-backup-when-current-config-is-different-from-.patch @@ -0,0 +1,91 @@ +From 6efbd7e4c0217d1f6b46e22ec51a209439678f5a Mon Sep 17 00:00:00 2001 +From: Prasanna Kumar Kalever +Date: Mon, 18 Dec 2017 15:25:42 +0530 +Subject: [PATCH] config: backup when current config is different from recent + backup copy + +With this change, +when '/etc/target/saveconfig.json' is same as '/etc/target/backup/saveconfig-${latest-stamp}.json +we skip backing up saveconfig.json + +Signed-off-by: Prasanna Kumar Kalever +--- + targetcli/ui_root.py | 53 ++++++++++++++++++++++++++++------------------------ + 1 file changed, 29 insertions(+), 24 deletions(-) + +diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py +index 33bb948..f398395 100644 +--- a/targetcli/ui_root.py ++++ b/targetcli/ui_root.py +@@ -23,6 +23,7 @@ import os + import re + import shutil + import stat ++import filecmp + + from configshell_fb import ExecutionError + from rtslib_fb import RTSRoot +@@ -85,32 +86,36 @@ class UIRoot(UINode): + except OSError as exe: + raise ExecutionError("Cannot create backup directory [%s] %s." % (backup_dir, exc.strerror)) + ++ # Only save backups if savefile exits + if os.path.exists(savefile): +- try: +- shutil.copy(savefile, backupfile) +- except IOError as ioe: +- backup_error = ioe.strerror or "Unknown error" +- +- if backup_error == None: +- # Kill excess backups ++ backed_files_list = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) ++ # Save backup if 1. backup dir is empty, or 2. savefile is differnt from recent backup copy ++ if not backed_files_list or not filecmp.cmp(backed_files_list[-1], savefile): + try: +- with open(universal_prefs_file) as prefs: +- backups = [line for line in prefs.read().splitlines() if re.match('^kept_backups\s*=', line)] +- kept_backups = int(backups[0].split('=')[1].strip()) +- except: +- kept_backups = default_kept_backups +- +- backups = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) +- files_to_unlink = list(reversed(backups))[kept_backups:] +- for f in files_to_unlink: +- with ignored(IOError): +- os.unlink(f) +- +- self.shell.log.info("Last %d configs saved in %s." % \ +- (kept_backups, backup_dir)) +- else: +- self.shell.log.warning("Could not create backup file %s: %s." % \ +- (backupfile, backup_error)) ++ shutil.copy(savefile, backupfile) ++ ++ except IOError as ioe: ++ backup_error = ioe.strerror or "Unknown error" ++ ++ if backup_error == None: ++ # Kill excess backups ++ try: ++ with open(universal_prefs_file) as prefs: ++ backups = [line for line in prefs.read().splitlines() if re.match('^kept_backups\s*=', line)] ++ kept_backups = int(backups[0].split('=')[1].strip()) ++ except: ++ kept_backups = default_kept_backups ++ ++ files_to_unlink = list(reversed(backed_files_list))[kept_backups:] ++ for f in files_to_unlink: ++ with ignored(IOError): ++ os.unlink(f) ++ ++ self.shell.log.info("Last %d configs saved in %s." % \ ++ (kept_backups, backup_dir)) ++ else: ++ self.shell.log.warning("Could not create backup file %s: %s." % \ ++ (backupfile, backup_error)) + + self.rtsroot.save_to_file(savefile) + +-- +1.8.3.1 + diff --git a/SOURCES/0005-config-rename-key-kept_backups-as-max_backup_files.patch b/SOURCES/0005-config-rename-key-kept_backups-as-max_backup_files.patch new file mode 100644 index 0000000..732b1c0 --- /dev/null +++ b/SOURCES/0005-config-rename-key-kept_backups-as-max_backup_files.patch @@ -0,0 +1,48 @@ +From bd3b2b50d741848a748830d9a1a7ee642e8d40d3 Mon Sep 17 00:00:00 2001 +From: Prasanna Kumar Kalever +Date: Tue, 19 Dec 2017 14:32:41 +0530 +Subject: [PATCH] config: rename key 'kept_backups' as 'max_backup_files' + +$ cat /etc/target/targetcli.conf +max_backup_files = 25 + +$ targetcli / saveconfig +Last 25 configs saved in /etc/target/backup. +Configuration saved to /etc/target/saveconfig.json + +Signed-off-by: Prasanna Kumar Kalever +--- + targetcli/ui_root.py | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py +index f398395..f84d33d 100644 +--- a/targetcli/ui_root.py ++++ b/targetcli/ui_root.py +@@ -101,18 +101,18 @@ class UIRoot(UINode): + # Kill excess backups + try: + with open(universal_prefs_file) as prefs: +- backups = [line for line in prefs.read().splitlines() if re.match('^kept_backups\s*=', line)] +- kept_backups = int(backups[0].split('=')[1].strip()) ++ backups = [line for line in prefs.read().splitlines() if re.match('^max_backup_files\s*=', line)] ++ max_backup_files = int(backups[0].split('=')[1].strip()) + except: +- kept_backups = default_kept_backups ++ max_backup_files = default_kept_backups + +- files_to_unlink = list(reversed(backed_files_list))[kept_backups:] ++ files_to_unlink = list(reversed(backed_files_list))[max_backup_files:] + for f in files_to_unlink: + with ignored(IOError): + os.unlink(f) + + self.shell.log.info("Last %d configs saved in %s." % \ +- (kept_backups, backup_dir)) ++ (max_backup_files, backup_dir)) + else: + self.shell.log.warning("Could not create backup file %s: %s." % \ + (backupfile, backup_error)) +-- +1.8.3.1 + diff --git a/SOURCES/0006-backup-global-option-to-tune-max-no.-of-backup-conf-.patch b/SOURCES/0006-backup-global-option-to-tune-max-no.-of-backup-conf-.patch new file mode 100644 index 0000000..3493991 --- /dev/null +++ b/SOURCES/0006-backup-global-option-to-tune-max-no.-of-backup-conf-.patch @@ -0,0 +1,78 @@ +From bf75323d7d678f01c21d0c9825bd55553727e934 Mon Sep 17 00:00:00 2001 +From: Prasanna Kumar Kalever +Date: Fri, 15 Dec 2017 17:23:25 +0530 +Subject: [PATCH] backup: global option to tune max no. of backup conf files + +Eg: +/> set global max_backup_files=1000 +Parameter max_backup_files is now '1000'. + +If 'max_backup_files' is set in /etc/target/targetcli.conf, then max value +between conf file setting and global option settings is considered. + +Signed-off-by: Prasanna Kumar Kalever +--- + scripts/targetcli | 1 + + targetcli/ui_node.py | 3 +++ + targetcli/ui_root.py | 8 +++++--- + 3 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/scripts/targetcli b/scripts/targetcli +index 5404fcd..371edbe 100755 +--- a/scripts/targetcli ++++ b/scripts/targetcli +@@ -49,6 +49,7 @@ class TargetCLI(ConfigShell): + 'auto_add_mapped_luns': True, + 'auto_cd_after_create': False, + 'auto_save_on_exit': True, ++ 'max_backup_files': '10', + 'auto_add_default_portal': True, + } + +diff --git a/targetcli/ui_node.py b/targetcli/ui_node.py +index c91dae2..a6982f1 100644 +--- a/targetcli/ui_node.py ++++ b/targetcli/ui_node.py +@@ -46,6 +46,9 @@ class UINode(ConfigNode): + self.define_config_group_param( + 'global', 'auto_add_default_portal', 'bool', + 'If true, adds a portal listening on all IPs to new targets.') ++ self.define_config_group_param( ++ 'global', 'max_backup_files', 'string', ++ 'Max no. of configurations to be backed up in /etc/target/backup/ directory.') + + def assert_root(self): + ''' +diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py +index f84d33d..a54845f 100644 +--- a/targetcli/ui_root.py ++++ b/targetcli/ui_root.py +@@ -35,7 +35,6 @@ from .ui_target import UIFabricModule + + default_save_file = "/etc/target/saveconfig.json" + universal_prefs_file = "/etc/target/targetcli.conf" +-default_kept_backups = 10 + + class UIRoot(UINode): + ''' +@@ -99,12 +98,15 @@ class UIRoot(UINode): + + if backup_error == None: + # Kill excess backups ++ max_backup_files = int(self.shell.prefs['max_backup_files']) ++ + try: + with open(universal_prefs_file) as prefs: + backups = [line for line in prefs.read().splitlines() if re.match('^max_backup_files\s*=', line)] +- max_backup_files = int(backups[0].split('=')[1].strip()) ++ if max_backup_files < int(backups[0].split('=')[1].strip()): ++ max_backup_files = int(backups[0].split('=')[1].strip()) + except: +- max_backup_files = default_kept_backups ++ self.shell.log.debug("No universal prefs file '%s'." % universal_prefs_file) + + files_to_unlink = list(reversed(backed_files_list))[max_backup_files:] + for f in files_to_unlink: +-- +1.8.3.1 + diff --git a/SOURCES/0007-Fix-default-max_backup_files-in-ui_command_saveconfi.patch b/SOURCES/0007-Fix-default-max_backup_files-in-ui_command_saveconfi.patch new file mode 100644 index 0000000..aa9a488 --- /dev/null +++ b/SOURCES/0007-Fix-default-max_backup_files-in-ui_command_saveconfi.patch @@ -0,0 +1,26 @@ +From cac1f18c54c87839b4eaa812cd438dc4593c7975 Mon Sep 17 00:00:00 2001 +From: tangwenji +Date: Thu, 15 Mar 2018 14:52:20 +0800 +Subject: [PATCH] Fix default max_backup_files in ui_command_saveconfig + +Signed-off-by: tangwenji +--- + targetcli/ui_root.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py +index a54845f..54d38e3 100644 +--- a/targetcli/ui_root.py ++++ b/targetcli/ui_root.py +@@ -98,7 +98,7 @@ class UIRoot(UINode): + + if backup_error == None: + # Kill excess backups +- max_backup_files = int(self.shell.prefs['max_backup_files']) ++ max_backup_files = int(self.shell.default_prefs['max_backup_files']) + + try: + with open(universal_prefs_file) as prefs: +-- +1.8.3.1 + diff --git a/SOURCES/0008-config-add-saveconfig-command-to-StorageObject-level.patch b/SOURCES/0008-config-add-saveconfig-command-to-StorageObject-level.patch new file mode 100644 index 0000000..b018d94 --- /dev/null +++ b/SOURCES/0008-config-add-saveconfig-command-to-StorageObject-level.patch @@ -0,0 +1,196 @@ +From 9dc85912f6ff2ba77fa8b136478ae3d2776c2b5e Mon Sep 17 00:00:00 2001 +From: Prasanna Kumar Kalever +Date: Sat, 24 Feb 2018 08:38:35 +0530 +Subject: [PATCH] config: add saveconfig command to StorageObject level + +$ targetcli /backstores/user:glfs/block1 help +[...] +AVAILABLE COMMANDS +[...] + - saveconfig [savefile] + - set [group] [parameter=value...] + - status + - version + +$ targetcli /backstores/user:glfs/block1 saveconfig +Storage Object 'user:block1' config saved to /etc/target/saveconfig.json + +Signed-off-by: Prasanna Kumar Kalever +--- + targetcli/ui_backstore.py | 22 ++++++++++ + targetcli/ui_root.py | 110 ++++++++++++++++++++++++++-------------------- + 2 files changed, 85 insertions(+), 47 deletions(-) + +diff --git a/targetcli/ui_backstore.py b/targetcli/ui_backstore.py +index 5af448f..efa532f 100644 +--- a/targetcli/ui_backstore.py ++++ b/targetcli/ui_backstore.py +@@ -36,6 +36,8 @@ from rtslib_fb.utils import get_block_type + + from .ui_node import UINode, UIRTSLibNode + ++default_save_file = "/etc/target/saveconfig.json" ++ + alua_rw_params = ['alua_access_state', 'alua_access_status', + 'alua_write_metadata', 'alua_access_type', 'preferred', + 'nonop_delay_msecs', 'trans_delay_msecs', +@@ -702,6 +704,26 @@ class UIStorageObject(UIRTSLibNode): + self.shell.con.display("Backstore plugin %s %s" + % (self.rtsnode.plugin, self.rtsnode.version)) + ++ def ui_command_saveconfig(self, savefile=None): ++ ''' ++ Save configuration of this StorageObject. ++ ''' ++ so = self.rtsnode ++ rn = self.get_root() ++ ++ if not savefile: ++ savefile = default_save_file ++ ++ savefile = os.path.expanduser(savefile) ++ ++ rn._save_backups(savefile) ++ ++ rn.rtsroot.save_to_file(savefile, ++ '/backstores/' + so.plugin + '/' + so.name) ++ ++ self.shell.log.info("Storage Object '%s:%s' config saved to %s." ++ % (so.plugin, so.name, savefile)) ++ + + class UIPSCSIStorageObject(UIStorageObject): + def summary(self): +diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py +index 54d38e3..09f3f1b 100644 +--- a/targetcli/ui_root.py ++++ b/targetcli/ui_root.py +@@ -62,6 +62,65 @@ class UIRoot(UINode): + if fm.wwns == None or any(fm.wwns): + UIFabricModule(fm, self) + ++ def _save_backups(self, savefile): ++ ''' ++ Take backup of config-file if needed. ++ ''' ++ # Only save backups if saving to default location ++ if savefile != default_save_file: ++ return ++ ++ backup_dir = os.path.dirname(savefile) + "/backup/" ++ backup_name = "saveconfig-" + \ ++ datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json" ++ backupfile = backup_dir + backup_name ++ backup_error = None ++ ++ if not os.path.exists(backup_dir): ++ try: ++ os.makedirs(backup_dir); ++ except OSError as exe: ++ raise ExecutionError("Cannot create backup directory [%s] %s." ++ % (backup_dir, exc.strerror)) ++ ++ # Only save backups if savefile exits ++ if not os.path.exists(savefile): ++ return ++ ++ backed_files_list = sorted(glob(os.path.dirname(savefile) + \ ++ "/backup/*.json")) ++ ++ # Save backup if backup dir is empty, or savefile is differnt from recent backup copy ++ if not backed_files_list or not filecmp.cmp(backed_files_list[-1], savefile): ++ try: ++ shutil.copy(savefile, backupfile) ++ ++ except IOError as ioe: ++ backup_error = ioe.strerror or "Unknown error" ++ ++ if backup_error == None: ++ # remove excess backups ++ max_backup_files = int(self.shell.prefs['max_backup_files']) ++ ++ try: ++ with open(universal_prefs_file) as prefs: ++ backups = [line for line in prefs.read().splitlines() if re.match('^max_backup_files\s*=', line)] ++ if max_backup_files < int(backups[0].split('=')[1].strip()): ++ max_backup_files = int(backups[0].split('=')[1].strip()) ++ except: ++ self.shell.log.debug("No universal prefs file '%s'." % universal_prefs_file) ++ ++ files_to_unlink = list(reversed(backed_files_list))[max_backup_files:] ++ for f in files_to_unlink: ++ with ignored(IOError): ++ os.unlink(f) ++ ++ self.shell.log.info("Last %d configs saved in %s." ++ % (max_backup_files, backup_dir)) ++ else: ++ self.shell.log.warning("Could not create backup file %s: %s." ++ % (backupfile, backup_error)) ++ + def ui_command_saveconfig(self, savefile=default_save_file): + ''' + Saves the current configuration to a file so that it can be restored +@@ -69,55 +128,12 @@ class UIRoot(UINode): + ''' + self.assert_root() + ++ if not savefile: ++ savefile = default_save_file ++ + savefile = os.path.expanduser(savefile) + +- # Only save backups if saving to default location +- if savefile == default_save_file: +- backup_dir = os.path.dirname(savefile) + "/backup" +- backup_name = "saveconfig-" + \ +- datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json" +- backupfile = backup_dir + "/" + backup_name +- backup_error = None +- +- if not os.path.exists(backup_dir): +- try: +- os.makedirs(backup_dir); +- except OSError as exe: +- raise ExecutionError("Cannot create backup directory [%s] %s." % (backup_dir, exc.strerror)) +- +- # Only save backups if savefile exits +- if os.path.exists(savefile): +- backed_files_list = sorted(glob(os.path.dirname(savefile) + "/backup/*.json")) +- # Save backup if 1. backup dir is empty, or 2. savefile is differnt from recent backup copy +- if not backed_files_list or not filecmp.cmp(backed_files_list[-1], savefile): +- try: +- shutil.copy(savefile, backupfile) +- +- except IOError as ioe: +- backup_error = ioe.strerror or "Unknown error" +- +- if backup_error == None: +- # Kill excess backups +- max_backup_files = int(self.shell.default_prefs['max_backup_files']) +- +- try: +- with open(universal_prefs_file) as prefs: +- backups = [line for line in prefs.read().splitlines() if re.match('^max_backup_files\s*=', line)] +- if max_backup_files < int(backups[0].split('=')[1].strip()): +- max_backup_files = int(backups[0].split('=')[1].strip()) +- except: +- self.shell.log.debug("No universal prefs file '%s'." % universal_prefs_file) +- +- files_to_unlink = list(reversed(backed_files_list))[max_backup_files:] +- for f in files_to_unlink: +- with ignored(IOError): +- os.unlink(f) +- +- self.shell.log.info("Last %d configs saved in %s." % \ +- (max_backup_files, backup_dir)) +- else: +- self.shell.log.warning("Could not create backup file %s: %s." % \ +- (backupfile, backup_error)) ++ self._save_backups(savefile) + + self.rtsroot.save_to_file(savefile) + +-- +1.8.3.1 + diff --git a/SOURCES/0009-Support-tcmu-hw-max-sectors.patch b/SOURCES/0009-Support-tcmu-hw-max-sectors.patch new file mode 100644 index 0000000..4137170 --- /dev/null +++ b/SOURCES/0009-Support-tcmu-hw-max-sectors.patch @@ -0,0 +1,43 @@ +From 2f3183bf56295a764e743d0601275c7aa5a815db Mon Sep 17 00:00:00 2001 +From: Mike Christie +Date: Wed, 29 Mar 2017 15:01:05 -0500 +Subject: [PATCH] Support tcmu hw max sectors + +Support hw_max_sectors setting for tcmu added in this patch: + +commit 3abaa2bfdb1e6bb33d38a2e82cf3bb82ec0197bf +Author: Mike Christie +Date: Wed Mar 1 23:14:39 2017 -0600 + + tcmu: allow hw_max_sectors greater than 128 +--- + targetcli/ui_backstore.py | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/targetcli/ui_backstore.py b/targetcli/ui_backstore.py +index 4c577a4..88240bb 100644 +--- a/targetcli/ui_backstore.py ++++ b/targetcli/ui_backstore.py +@@ -574,7 +574,8 @@ class UIUserBackedBackstore(UIBackstore): + print(x.get("ConfigDesc", "No description.")) + print() + +- def ui_command_create(self, name, size, cfgstring, wwn=None): ++ def ui_command_create(self, name, size, cfgstring, wwn=None, ++ hw_max_sectors=None): + ''' + Creates a User-backed storage object. + +@@ -598,7 +599,8 @@ class UIUserBackedBackstore(UIBackstore): + if not ok: + raise ExecutionError("cfgstring invalid: %s" % errmsg) + +- so = UserBackedStorageObject(name, size=size, config=config, wwn=wwn) ++ so = UserBackedStorageObject(name, size=size, config=config, wwn=wwn, ++ hw_max_sectors=hw_max_sectors) + ui_so = UIUserBackedStorageObject(so, self) + self.shell.log.info("Created user-backed storage object %s size %d." + % (name, size)) +-- +1.8.3.1 + diff --git a/SOURCES/0010-create-add-a-way-to-set-control-string.patch b/SOURCES/0010-create-add-a-way-to-set-control-string.patch new file mode 100644 index 0000000..55bae36 --- /dev/null +++ b/SOURCES/0010-create-add-a-way-to-set-control-string.patch @@ -0,0 +1,30 @@ +diff --git a/targetcli/ui_backstore.py b/targetcli/ui_backstore.py +index 3838d02..b60eba5 100644 +--- a/targetcli/ui_backstore.py ++++ b/targetcli/ui_backstore.py +@@ -577,7 +577,7 @@ class UIUserBackedBackstore(UIBackstore): + print() + + def ui_command_create(self, name, size, cfgstring, wwn=None, +- hw_max_sectors=None): ++ hw_max_sectors=None, control=None): + ''' + Creates a User-backed storage object. + +@@ -602,7 +602,7 @@ class UIUserBackedBackstore(UIBackstore): + raise ExecutionError("cfgstring invalid: %s" % errmsg) + + so = UserBackedStorageObject(name, size=size, config=config, wwn=wwn, +- hw_max_sectors=hw_max_sectors) ++ hw_max_sectors=hw_max_sectors, control=control) + ui_so = UIUserBackedStorageObject(so, self) + self.shell.log.info("Created user-backed storage object %s size %d." + % (name, size)) +@@ -633,6 +633,7 @@ class UIStorageObject(UIRTSLibNode): + 'fabric_max_sectors': ('number', 'Maximum number of sectors the fabric can transfer at once.'), + 'hw_block_size': ('number', 'Hardware block size in bytes.'), + 'hw_max_sectors': ('number', 'Maximum number of sectors the hardware can transfer at once.'), ++ 'control': ('string', 'Comma separated string of control=value tuples that will be passed to kernel control file.'), + 'hw_pi_prot_type': ('number', 'If non-zero, DIF protection is enabled on the underlying hardware.'), + 'hw_queue_depth': ('number', 'Hardware queue depth.'), + 'is_nonrot': ('number', 'If set to 1, the backstore is a non rotational device.'), diff --git a/SPECS/targetcli.spec b/SPECS/targetcli.spec new file mode 100644 index 0000000..04521c4 --- /dev/null +++ b/SPECS/targetcli.spec @@ -0,0 +1,263 @@ +%global oname targetcli-fb + +Name: targetcli +License: ASL 2.0 +Group: System Environment/Libraries +Summary: An administration shell for storage targets +Version: 2.1.fb46 +Release: 4%{?dist} +URL: https://fedorahosted.org/targetcli-fb/ +Source: https://fedorahosted.org/released/targetcli-fb/%{oname}-%{version}.tar.gz +Patch0: 0001-Properly-detect-errors-when-writing-backup-files.-Cl.patch +Patch1: 0002-Read-number-of-backup-files-to-keep-from-file.patch +Patch2: 0003-config-defend-on-etc-target-backup-directory.patch +Patch3: 0004-config-backup-when-current-config-is-different-from-.patch +Patch4: 0005-config-rename-key-kept_backups-as-max_backup_files.patch +Patch5: 0006-backup-global-option-to-tune-max-no.-of-backup-conf-.patch +Patch6: 0007-Fix-default-max_backup_files-in-ui_command_saveconfi.patch +Patch7: 0008-config-add-saveconfig-command-to-StorageObject-level.patch +Patch8: 0009-Support-tcmu-hw-max-sectors.patch +Patch9: 0010-create-add-a-way-to-set-control-string.patch +BuildArch: noarch +BuildRequires: python-devel python-setuptools +Requires: python-rtslib >= 2.1.fb41, python-configshell, python-ethtool + + +%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 %{oname}-%{version} +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 + +%build +%{__python} setup.py build +gzip --stdout targetcli.8 > targetcli.8.gz + +%install +%{__python} setup.py install --skip-build --root %{buildroot} +mkdir -p %{buildroot}%{_sysconfdir}/target/backup +mkdir -p %{buildroot}%{_mandir}/man8/ +install -m 644 targetcli.8.gz %{buildroot}%{_mandir}/man8/ + +%files +%{python_sitelib}/* +%{_bindir}/targetcli +%dir %{_sysconfdir}/target +%dir %{_sysconfdir}/target/backup +%doc COPYING README.md +%{_mandir}/man8/targetcli.8.gz + +%changelog +* Mon Apr 23 2018 Maurizio Lombardi - 2.1.fb46-4 +- Properly detect errors when writing backup files. (Closes: #80) (#81) +- Read number of backup files to keep from file +- config: defend on '/etc/target/backup' directory +- config: backup when current config is different from recent backup copy +- config: rename key 'kept_backups' as 'max_backup_files' +- backup: global option to tune max no. of backup conf files + +* Fri Apr 13 2018 Maurizio Lombardi - 2.1.fb46-3 +- Support tcmu hw max sectors +- create: add a way to set control string + +* Wed Apr 11 2018 Maurizio Lombardi - 2.1.fb46-2 +- Add saveconfig command to StorageObject level + +* Thu Mar 2 2017 Andy Grover - 2.1.fb46-1 +- New upstream version +- Drop no-model-alias.patch +- Drop no-userbackstores.patch + +* Tue Aug 18 2015 Andy Grover - 2.1.fb41-3 +- add no-model-alias.patch, LIO no longer supports for pscsi + +* Wed Jul 15 2015 Andy Grover - 2.1.fb41-2 +- add no-userbackstores.patch to not show, since not supported on RHEL + +* Wed Jul 15 2015 Andy Grover - 2.1.fb41-1 +- Update to latest in Fedora +- Remove patches as fixes are in upstream version + +* Tue Nov 4 2014 Andy Grover - 2.1.fb37-3 +- add tpg-invalid-tag.patch +- add default-portal-pref.patch + +* Tue Nov 4 2014 Andy Grover - 2.1.fb37-2 +- add fix-session-detail.patch + +* Thu Oct 9 2014 Andy Grover - 2.1.fb37-1 +- New upstream version + +* Mon Feb 24 2014 Andy Grover - 2.1.fb34-1 +- New upstream version. Fixes rhbz #1066695 + +* Fri Dec 27 2013 Daniel Mach - 2.1.fb31-2 +- Mass rebuild 2013-12-27 + +* Fri Nov 1 2013 Andy Grover - 2.1.fb31-1 +- New upstream version +- Move service handling to python-rtslib +- Remove old packaging bits: clean, buildroot, defattr + +* Wed Sep 11 2013 Andy Grover - 2.1.fb30-1 +- New upstream version + +* Tue Sep 10 2013 Andy Grover - 2.1.fb29-1 +- New upstream release +- Remove no-longer-needed BuildRequires + +* Mon Aug 5 2013 Andy Grover - 2.1.fb28-1 +- New upstream release + +* Sun Aug 04 2013 Fedora Release Engineering - 2.1.fb27-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jul 23 2013 Andy Grover - 2.1.fb27-1 +- New upstream release +- License now Apache 2.0 +- Remove patch modules-not-loaded.patch + +* Tue Jun 18 2013 Andy Grover - 2.1.fb26-2 +- Add patch + * modules-not-loaded.patch + +* Fri Jun 7 2013 Andy Grover - 2.1.fb26-1 +- New upstream release + +* Thu May 9 2013 Andy Grover - 2.1.fb25-1 +- New upstream release + +* Thu May 2 2013 Andy Grover - 2.1.fb24-1 +- New upstream release +- Update source URL + +* Fri Apr 12 2013 Andy Grover - 2.1.fb23-1 +- New upstream release + +* Wed Apr 10 2013 Andy Grover - 2.1.fb22-1 +- New upstream release + +* Mon Mar 4 2013 Andy Grover - 2.0.fb21-1 +- New upstream release + +* Tue Feb 26 2013 Andy Grover - 2.0.fb20-1 +- New upstream release + +* Fri Feb 15 2013 Fedora Release Engineering - 2.0rc1.fb19-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Mon Jan 7 2013 Andy Grover - 2.0rc1.fb19-1 +- New upstream release + +* Thu Jan 3 2013 Andy Grover - 2.0rc1.fb18-2 +- Add python-ethtool BuildRequires + +* Thu Dec 20 2012 Andy Grover - 2.0rc1.fb18-1 +- New upstream release +- Add python-ethtool requires +- Update Source0 to use Github tar-from-tag instead of Downloads + +* Thu Dec 13 2012 Lukáš Nykrýn - 2.0rc1.fb17-2 +- Scriptlets replaced with new systemd macros (#850335) + +* Mon Nov 12 2012 Andy Grover - 2.0rc1.fb17-1 +- New upstream release + +* Tue Aug 7 2012 Andy Grover - 2.0rc1.fb16-1 +- New upstream release +- Update rtslib version dependency + +* Tue Jul 31 2012 Andy Grover - 2.0rc1.fb15-1 +- New upstream release + +* Sat Jul 21 2012 Fedora Release Engineering - 2.0rc1.fb14-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Thu Jun 28 2012 Andy Grover - 2.0rc1.fb14-2 +- Fix %files to claim /etc/target, not claim sitelib + +* Thu Jun 28 2012 Andy Grover - 2.0rc1.fb14-1 +- New upstream release + +* Tue Jun 12 2012 Andy Grover - 2.0rc1.fb13-1 +- New upstream release + +* Wed May 30 2012 Andy Grover - 2.0rc1.fb12-1 +- Update Source URL to proper tarball +- New upstream release + +* Mon Apr 9 2012 Andy Grover - 2.0rc1.fb11-1 +- New upstream release + +* Wed Feb 29 2012 Andy Grover - 2.0rc1.fb10-1 +- New upstream release + +* Tue Feb 21 2012 Andy Grover - 2.0rc1.fb9-1 +- New upstream release + +* Thu Feb 16 2012 Andy Grover - 2.0rc1.fb8-1 +- New upstream release + +* Wed Feb 8 2012 Andy Grover - 2.0rc1.fb7-1 +- New upstream release + +* Fri Feb 3 2012 Andy Grover - 2.0rc1.fb6-1 +- New upstream release + +* Tue Jan 24 2012 Andy Grover - 2.0rc1.fb5-2 +- Update After= in service file to wait for localfs and network +- Improve description in service file + +* Tue Jan 24 2012 Andy Grover - 2.0rc1.fb5-1 +- New upstream release + +* Fri Jan 13 2012 Andy Grover - 2.0rc1.fb4-1 +- New upstream release + +* Tue Dec 13 2011 Andy Grover - 2.0rc1.fb3-2 +- Fix service file to mount configfs before starting targetcli + +* Tue Dec 13 2011 Andy Grover - 2.0rc1.fb3-1 +- New upstream release +- Fixup service file for new start/stop targetcli commands + +* Tue Dec 6 2011 Andy Grover - 2.0rc1.fb2-1 +- New upstream source and release +- Remove patches: + * targetcli-git-version.patch + * 0001-Remove-ads-from-cli-welcome-msg.-Mention-help-is-ava.patch + * 0002-bundle-lio-utils.patch + * 0003-Hack.-dump-scripts-aren-t-in-PATH-anymore-so-call-th.patch + * 0004-ignore-errors-from-failure-to-set-device-attributes.patch + * 0005-fix-spec_root-path.patch + * 0006-add-docs.patch + * 0007-all-start.patch + +* Mon Nov 21 2011 Andy Grover - 1.99.2.gitb03ec79-4 +- Update doc patch to include iscsi tutorial + +* Wed Nov 2 2011 Andy Grover - 1.99.2.gitb03ec79-3 +- Add buildrequires for systemd-units +- use _unitdir +- remove preun, modify post + +* Wed Nov 2 2011 Andy Grover - 1.99.2.gitb03ec79-2 +- Add patch + * 0007-all-start.patch +- Replace sysv init with systemd init + +* Fri Oct 7 2011 Andy Grover - 1.99.2.gitb03ec79-1 +- Initial Fedora packaging