multiSSH3 5.78__tar.gz → 5.80__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of multiSSH3 might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 5.78
3
+ Version: 5.80
4
4
  Summary: Run commands on multiple hosts via SSH
5
5
  Home-page: https://github.com/yufei-pan/multiSSH3
6
6
  Author: Yufei Pan
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 5.78
3
+ Version: 5.80
4
4
  Summary: Run commands on multiple hosts via SSH
5
5
  Home-page: https://github.com/yufei-pan/multiSSH3
6
6
  Author: Yufei Pan
@@ -55,10 +55,10 @@ except AttributeError:
55
55
  # If neither is available, use a dummy decorator
56
56
  def cache_decorator(func):
57
57
  return func
58
- version = '5.78'
58
+ version = '5.80'
59
59
  VERSION = version
60
60
  __version__ = version
61
- COMMIT_DATE = '2025-06-26'
61
+ COMMIT_DATE = '2025-07-09'
62
62
 
63
63
  CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
64
64
  '~/multiSSH3.config.json',
@@ -1399,7 +1399,7 @@ def run_command(host, sem, timeout=60,passwds=None, retry_limit = 5):
1399
1399
  else:
1400
1400
  fileArgs = host.files + [f'{host.resolvedName}:{host.command}']
1401
1401
  if useScp:
1402
- formatedCMD = [_binPaths['scp'],'-rpB'] + localExtraArgs + extraargs +['--']+fileArgs
1402
+ formatedCMD = [_binPaths['scp'],'-rp'] + localExtraArgs + extraargs +['--']+fileArgs
1403
1403
  else:
1404
1404
  formatedCMD = [_binPaths['rsync'],'-ahlX','--partial','--inplace', '--info=name'] + rsyncLocalExtraArgs + extraargs +['--']+fileArgs
1405
1405
  else:
@@ -2443,7 +2443,7 @@ def processRunOnHosts(timeout, password, max_connections, hosts, returnUnfinishe
2443
2443
  # check for the old content, only update if the new content is different
2444
2444
  if not os.path.exists(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv')):
2445
2445
  with open(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv'),'w') as f:
2446
- f.writelines(f'{host},{expTime}' for host,expTime in unavailableHosts.values())
2446
+ f.writelines(f'{host},{expTime}' for host,expTime in unavailableHosts.items())
2447
2447
  else:
2448
2448
  oldDic = {}
2449
2449
  try:
@@ -2568,7 +2568,7 @@ def getStrCommand(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAULT_ONE_O
2568
2568
  history_file = history_file, env_file = env_file,
2569
2569
  repeat = repeat,interval = interval,
2570
2570
  shortend = shortend)
2571
- commands = [command.replace('"', '\\"') for command in commands]
2571
+ commands = [command.replace('"', '\\"').replace('\n', '\\n').replace('\t', '\\t') for command in commands]
2572
2572
  commandStr = '"' + '" "'.join(commands) + '"' if commands else ''
2573
2573
  filePath = os.path.abspath(__file__)
2574
2574
  programName = filePath if filePath else 'mssh'
@@ -3008,23 +3008,9 @@ def write_default_config(args,CONFIG_FILE = None):
3008
3008
  eprint(f'Printing the config file to stdout:')
3009
3009
  print(json.dumps(__configs_from_file, indent=4))
3010
3010
 
3011
- #%% ------------ Wrapper Block ----------------
3012
- def main():
3013
- global _emo
3014
- global __global_suppress_printout
3015
- global __mainReturnCode
3016
- global __failedHosts
3017
- global __ipmiiInterfaceIPPrefix
3011
+ #%% ------------ Argument Processing -----------------
3012
+ def get_parser():
3018
3013
  global _binPaths
3019
- global _env_file
3020
- global __DEBUG_MODE
3021
- global __configs_from_file
3022
- global _encoding
3023
- global __returnZero
3024
- _emo = False
3025
- # We handle the signal
3026
- signal.signal(signal.SIGINT, signal_handler)
3027
- # We parse the arguments
3028
3014
  parser = argparse.ArgumentParser(description=f'Run a command on multiple hosts, Use #HOST# or #HOSTNAME# to replace the host name in the command. Config file chain: {CONFIG_FILE_CHAIN!r}',
3029
3015
  epilog=f'Found bins: {list(_binPaths.values())}\n Missing bins: {_binCalled - set(_binPaths.keys())}')
3030
3016
  parser.add_argument('hosts', metavar='hosts', type=str, nargs='?', help=f'Hosts to run the command on, use "," to seperate hosts. (default: {DEFAULT_HOSTS})',default=DEFAULT_HOSTS)
@@ -3075,18 +3061,20 @@ def main():
3075
3061
  parser.add_argument('--script', action='store_true', help='Run the command in script mode, short for -SCRIPT or --no_watch --skip_unreachable --no_env --no_history --greppable --error_only')
3076
3062
  parser.add_argument('-e','--encoding', type=str, help=f'The encoding to use for the output. (default: {DEFAULT_ENCODING})', default=DEFAULT_ENCODING)
3077
3063
  parser.add_argument("-V","--version", action='version', version=f'%(prog)s {version} @ {COMMIT_DATE} with [ {", ".join(_binPaths.keys())} ] by {AUTHOR} ({AUTHOR_EMAIL})')
3078
-
3079
- # parser.add_argument('-u', '--user', metavar='user', type=str, nargs=1,
3080
- # help='the user to use to connect to the hosts')
3081
- #args = parser.parse_args()
3064
+ return parser
3082
3065
 
3066
+ def process_args(args = None):
3067
+ parser = get_parser()
3068
+ # We handle the signal
3069
+ signal.signal(signal.SIGINT, signal_handler)
3070
+ # We parse the arguments
3083
3071
  # if python version is 3.7 or higher, use parse_intermixed_args
3084
3072
  try:
3085
- args = parser.parse_intermixed_args()
3073
+ args = parser.parse_intermixed_args(args)
3086
3074
  except Exception :
3087
3075
  #eprint(f"Error while parsing arguments: {e!r}")
3088
3076
  # try to parse the arguments using parse_known_args
3089
- args, unknown = parser.parse_known_args()
3077
+ args, unknown = parser.parse_known_args(args)
3090
3078
  # if there are unknown arguments, we will try to parse them again using parse_args
3091
3079
  if unknown:
3092
3080
  eprint(f"Warning: Unknown arguments, treating all as commands: {unknown!r}")
@@ -3099,10 +3087,14 @@ def main():
3099
3087
  args.no_history = True
3100
3088
  args.greppable = True
3101
3089
  args.error_only = True
3102
-
3103
- if args.return_zero:
3104
- __returnZero = True
3105
3090
 
3091
+ if args.unavailable_host_expiry <= 0:
3092
+ eprint(f"Warning: The unavailable host expiry time {args.unavailable_host_expiry} is less than 0, setting it to 10 seconds.")
3093
+ args.unavailable_host_expiry = 10
3094
+ return args
3095
+
3096
+ def process_config_file(args):
3097
+ global __configs_from_file
3106
3098
  if args.generate_config_file or args.store_config_file:
3107
3099
  if args.store_config_file:
3108
3100
  configFileToWriteTo = args.store_config_file
@@ -3124,11 +3116,12 @@ def main():
3124
3116
  __configs_from_file.update(load_config_file(os.path.expanduser(args.config_file)))
3125
3117
  else:
3126
3118
  eprint(f"Warning: Config file {args.config_file!r} not found, ignoring it.")
3119
+ return args
3127
3120
 
3128
- _env_file = args.env_file
3129
- __DEBUG_MODE = args.debug
3130
3121
  # if there are more than 1 commands, and every command only consists of one word,
3131
3122
  # we will ask the user to confirm if they want to run multiple commands or just one command.
3123
+
3124
+ def process_commands(args):
3132
3125
  if not args.file and len(args.commands) > 1 and all([len(command.split()) == 1 for command in args.commands]):
3133
3126
  eprint(f"Multiple one word command detected, what to do? (1/m/n)")
3134
3127
  eprint(f"1: Run 1 command [{' '.join(args.commands)}] on all hosts ( default )")
@@ -3142,7 +3135,9 @@ def main():
3142
3135
  eprint(f"\nRunning multiple commands: {', '.join(args.commands)!r} on all hosts")
3143
3136
  else:
3144
3137
  _exit_with_code(0, "Aborted by user, no commands to run")
3138
+ return args
3145
3139
 
3140
+ def process_keys(args):
3146
3141
  if args.key or args.use_key:
3147
3142
  if not args.key:
3148
3143
  args.key = find_ssh_key_file()
@@ -3151,23 +3146,43 @@ def main():
3151
3146
  args.key = find_ssh_key_file(args.key)
3152
3147
  elif not os.path.exists(args.key):
3153
3148
  eprint(f"Warning: Identity file {args.key!r} not found. Passing to ssh anyway. Proceed with caution.")
3149
+ return args
3154
3150
 
3151
+
3152
+ def set_global_with_args(args):
3153
+ global _emo
3154
+ global __ipmiiInterfaceIPPrefix
3155
+ global _env_file
3156
+ global __DEBUG_MODE
3157
+ global __configs_from_file
3158
+ global _encoding
3159
+ global __returnZero
3160
+ _emo = False
3155
3161
  __ipmiiInterfaceIPPrefix = args.ipmi_interface_ip_prefix
3162
+ _env_file = args.env_file
3163
+ __DEBUG_MODE = args.debug
3164
+ _encoding = args.encoding
3165
+ if args.return_zero:
3166
+ __returnZero = True
3156
3167
 
3157
- if args.no_output:
3158
- __global_suppress_printout = True
3159
-
3160
- if args.unavailable_host_expiry <= 0:
3161
- eprint(f"Warning: The unavailable host expiry time {args.unavailable_host_expiry} is less than 0, setting it to 10 seconds.")
3162
- args.unavailable_host_expiry = 10
3168
+ #%% ------------ Wrapper Block ----------------
3169
+ def main():
3170
+ global __global_suppress_printout
3171
+ global __mainReturnCode
3172
+ global __failedHosts
3173
+ args = process_args()
3174
+ args = process_config_file(args)
3175
+ args = process_commands(args)
3176
+ args = process_keys(args)
3177
+ set_global_with_args(args)
3163
3178
 
3164
3179
  if args.use_script_timeout:
3165
3180
  # set timeout to the default script timeout if timeout is not set
3166
3181
  if args.timeout == DEFAULT_CLI_TIMEOUT:
3167
3182
  args.timeout = DEFAULT_TIMEOUT
3168
3183
 
3169
- _encoding = args.encoding
3170
-
3184
+ if args.no_output:
3185
+ __global_suppress_printout = True
3171
3186
  if not __global_suppress_printout:
3172
3187
  cmdStr = getStrCommand(args.hosts,args.commands,
3173
3188
  oneonone=args.oneonone,timeout=args.timeout,password=args.password,
@@ -3199,9 +3214,7 @@ def main():
3199
3214
  history_file = args.history_file,
3200
3215
  )
3201
3216
  #print('*'*80)
3202
-
3203
3217
  #if not __global_suppress_printout: eprint('-'*80)
3204
-
3205
3218
  succeededHosts = set()
3206
3219
  for host in hosts:
3207
3220
  if host.returncode and host.returncode != 0:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes