multiSSH3 5.78__py3-none-any.whl → 5.80__py3-none-any.whl
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.
- multiSSH3.py +55 -42
- {multissh3-5.78.dist-info → multissh3-5.80.dist-info}/METADATA +1 -1
- multissh3-5.80.dist-info/RECORD +6 -0
- multissh3-5.78.dist-info/RECORD +0 -6
- {multissh3-5.78.dist-info → multissh3-5.80.dist-info}/WHEEL +0 -0
- {multissh3-5.78.dist-info → multissh3-5.80.dist-info}/entry_points.txt +0 -0
- {multissh3-5.78.dist-info → multissh3-5.80.dist-info}/top_level.txt +0 -0
multiSSH3.py
CHANGED
|
@@ -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.
|
|
58
|
+
version = '5.80'
|
|
59
59
|
VERSION = version
|
|
60
60
|
__version__ = version
|
|
61
|
-
COMMIT_DATE = '2025-
|
|
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'],'-
|
|
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.
|
|
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
|
-
#%% ------------
|
|
3012
|
-
def
|
|
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
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
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
|
-
|
|
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:
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
multiSSH3.py,sha256=i_wruVnwT6q5E9dy1tEmPtesXHGpGKuyuc2vDZZTbD0,152640
|
|
2
|
+
multissh3-5.80.dist-info/METADATA,sha256=lepwkt4r6DPCMmFwwrngTOB0Pc6_bBkeCitxHvtpblc,18093
|
|
3
|
+
multissh3-5.80.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
+
multissh3-5.80.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
+
multissh3-5.80.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
+
multissh3-5.80.dist-info/RECORD,,
|
multissh3-5.78.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
multiSSH3.py,sha256=SN6BGWEnwE3n_T2GxHkYWYa2JvX1Rru5r6C-gxLINHs,152293
|
|
2
|
-
multissh3-5.78.dist-info/METADATA,sha256=nnplqT8neDvB-cdOGgNJMcm-w0II6Qe7D7vRbIM-2aA,18093
|
|
3
|
-
multissh3-5.78.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
-
multissh3-5.78.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
-
multissh3-5.78.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
-
multissh3-5.78.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|