multiSSH3 5.77__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 +125 -89
- {multissh3-5.77.dist-info → multissh3-5.80.dist-info}/METADATA +1 -1
- multissh3-5.80.dist-info/RECORD +6 -0
- multissh3-5.77.dist-info/RECORD +0 -6
- {multissh3-5.77.dist-info → multissh3-5.80.dist-info}/WHEEL +0 -0
- {multissh3-5.77.dist-info → multissh3-5.80.dist-info}/entry_points.txt +0 -0
- {multissh3-5.77.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',
|
|
@@ -246,6 +246,7 @@ class Host:
|
|
|
246
246
|
self.stderr = [] # the stderr of the command
|
|
247
247
|
self.lineNumToPrintSet = set() # line numbers to reprint
|
|
248
248
|
self.lastUpdateTime = time.monotonic() # the last time the output was updated
|
|
249
|
+
self.lastPrintedUpdateTime = 0 # the last time the output was printed
|
|
249
250
|
self.files = files # the files to be copied to the host
|
|
250
251
|
self.ipmi = ipmi # whether to use ipmi to connect to the host
|
|
251
252
|
self.shell = shell # whether to use shell to run the command
|
|
@@ -260,6 +261,9 @@ class Host:
|
|
|
260
261
|
self.identity_file = identity_file
|
|
261
262
|
self.ip = ip if ip else getIP(name)
|
|
262
263
|
self.current_color_pair = [-1, -1, 1]
|
|
264
|
+
self.output_buffer = io.BytesIO()
|
|
265
|
+
self.stdout_buffer = io.BytesIO()
|
|
266
|
+
self.stderr_buffer = io.BytesIO()
|
|
263
267
|
|
|
264
268
|
def __iter__(self):
|
|
265
269
|
return zip(['name', 'command', 'returncode', 'stdout', 'stderr'], [self.name, self.command, self.returncode, self.stdout, self.stderr])
|
|
@@ -1133,7 +1137,7 @@ def expand_hostnames(hosts):
|
|
|
1133
1137
|
|
|
1134
1138
|
|
|
1135
1139
|
#%% ------------ Run Command Block ----------------
|
|
1136
|
-
def __handle_reading_stream(stream,target, host):
|
|
1140
|
+
def __handle_reading_stream(stream,target, host,buffer:io.BytesIO):
|
|
1137
1141
|
'''
|
|
1138
1142
|
Read the stream and append the lines to the target list
|
|
1139
1143
|
|
|
@@ -1146,50 +1150,63 @@ def __handle_reading_stream(stream,target, host):
|
|
|
1146
1150
|
None
|
|
1147
1151
|
'''
|
|
1148
1152
|
global _encoding
|
|
1149
|
-
def add_line(
|
|
1150
|
-
|
|
1151
|
-
target.pop()
|
|
1152
|
-
host.output.pop()
|
|
1153
|
-
current_line_str = current_line.decode(_encoding,errors='backslashreplace')
|
|
1153
|
+
def add_line(buffer,target, host):
|
|
1154
|
+
current_line_str = buffer.getvalue().decode(_encoding,errors='backslashreplace')
|
|
1154
1155
|
target.append(current_line_str)
|
|
1155
1156
|
host.output.append(current_line_str)
|
|
1156
1157
|
host.lineNumToPrintSet.add(len(host.output)-1)
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1158
|
+
buffer.seek(0)
|
|
1159
|
+
buffer.truncate(0)
|
|
1160
|
+
host.output_buffer.seek(0)
|
|
1161
|
+
host.output_buffer.truncate(0)
|
|
1162
|
+
|
|
1162
1163
|
for char in iter(lambda:stream.read(1), b''):
|
|
1164
|
+
host.lastUpdateTime = time.monotonic()
|
|
1163
1165
|
if char == b'\n':
|
|
1164
|
-
add_line(
|
|
1165
|
-
current_line = bytearray()
|
|
1166
|
-
lastLineCommited = True
|
|
1167
|
-
curser_position = 0
|
|
1168
|
-
previousUpdateTime = time.monotonic()
|
|
1166
|
+
add_line(buffer,target, host)
|
|
1169
1167
|
continue
|
|
1170
1168
|
elif char == b'\r':
|
|
1171
|
-
|
|
1169
|
+
buffer.seek(0)
|
|
1170
|
+
host.output_buffer.seek(0)
|
|
1172
1171
|
elif char == b'\x08':
|
|
1173
1172
|
# backspace
|
|
1174
|
-
if
|
|
1175
|
-
|
|
1173
|
+
if buffer.tell() > 0:
|
|
1174
|
+
buffer.seek(buffer.tell() - 1)
|
|
1175
|
+
buffer.truncate()
|
|
1176
|
+
if host.output_buffer.tell() > 0:
|
|
1177
|
+
host.output_buffer.seek(host.output_buffer.tell() - 1)
|
|
1178
|
+
host.output_buffer.truncate()
|
|
1176
1179
|
else:
|
|
1177
|
-
#
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
#
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1180
|
+
# normal character
|
|
1181
|
+
buffer.write(char)
|
|
1182
|
+
host.output_buffer.write(char)
|
|
1183
|
+
# if the length of the buffer is greater than 100, we try to decode the buffer to find if there are any unicode line change chars
|
|
1184
|
+
if buffer.tell() % 100 == 0 and buffer.tell() > 0:
|
|
1185
|
+
try:
|
|
1186
|
+
# try to decode the buffer to find if there are any unicode line change chars
|
|
1187
|
+
decodedLine = buffer.getvalue().decode(_encoding,errors='backslashreplace')
|
|
1188
|
+
lines = decodedLine.splitlines()
|
|
1189
|
+
if len(lines) > 1:
|
|
1190
|
+
# if there are multiple lines, we add them to the target
|
|
1191
|
+
for line in lines[:-1]:
|
|
1192
|
+
# for all lines except the last one, we add them to the target
|
|
1193
|
+
target.append(line)
|
|
1194
|
+
host.output.append(line)
|
|
1195
|
+
host.lineNumToPrintSet.add(len(host.output)-1)
|
|
1196
|
+
# we keep the last line in the buffer
|
|
1197
|
+
buffer.seek(0)
|
|
1198
|
+
buffer.truncate(0)
|
|
1199
|
+
buffer.write(lines[-1].encode(_encoding,errors='backslashreplace'))
|
|
1200
|
+
host.output_buffer.seek(0)
|
|
1201
|
+
host.output_buffer.truncate(0)
|
|
1202
|
+
host.output_buffer.write(lines[-1].encode(_encoding,errors='backslashreplace'))
|
|
1203
|
+
|
|
1204
|
+
except UnicodeDecodeError:
|
|
1205
|
+
# if there is a unicode decode error, we just skip this character
|
|
1206
|
+
continue
|
|
1207
|
+
if buffer.tell() > 0:
|
|
1208
|
+
# if there is still some data in the buffer, we add it to the target
|
|
1209
|
+
add_line(buffer,target, host)
|
|
1193
1210
|
|
|
1194
1211
|
def __handle_writing_stream(stream,stop_event,host):
|
|
1195
1212
|
'''
|
|
@@ -1208,28 +1225,28 @@ def __handle_writing_stream(stream,stop_event,host):
|
|
|
1208
1225
|
# __keyPressesIn is a list of lists.
|
|
1209
1226
|
# Each list is a list of characters to be sent to the stdin of the process at once.
|
|
1210
1227
|
# We do not send the last line as it may be incomplete.
|
|
1211
|
-
|
|
1228
|
+
sentInputPos = 0
|
|
1212
1229
|
while not stop_event.is_set():
|
|
1213
|
-
if
|
|
1214
|
-
stream.write(''.join(__keyPressesIn[
|
|
1230
|
+
if sentInputPos < len(__keyPressesIn) - 1 :
|
|
1231
|
+
stream.write(''.join(__keyPressesIn[sentInputPos]).encode(encoding=_encoding,errors='backslashreplace'))
|
|
1215
1232
|
stream.flush()
|
|
1216
|
-
line = '> ' + ''.join(__keyPressesIn[
|
|
1233
|
+
line = '> ' + ''.join(__keyPressesIn[sentInputPos]).encode(encoding=_encoding,errors='backslashreplace').decode(encoding=_encoding,errors='backslashreplace').replace('\n', '↵')
|
|
1217
1234
|
host.output.append(line)
|
|
1218
1235
|
host.stdout.append(line)
|
|
1219
1236
|
host.lineNumToPrintSet.add(len(host.output)-1)
|
|
1220
|
-
|
|
1237
|
+
sentInputPos += 1
|
|
1221
1238
|
host.lastUpdateTime = time.monotonic()
|
|
1222
1239
|
else:
|
|
1223
1240
|
time.sleep(0.01) # sleep for 10ms
|
|
1224
|
-
if
|
|
1225
|
-
eprint(f"Warning: {len(__keyPressesIn)-
|
|
1241
|
+
if sentInputPos < len(__keyPressesIn) - 1 :
|
|
1242
|
+
eprint(f"Warning: {len(__keyPressesIn)-sentInputPos} lines of key presses are not sent before the process is terminated!")
|
|
1226
1243
|
# # send the last line
|
|
1227
1244
|
# if __keyPressesIn and __keyPressesIn[-1]:
|
|
1228
1245
|
# stream.write(''.join(__keyPressesIn[-1]).encode())
|
|
1229
1246
|
# stream.flush()
|
|
1230
1247
|
# host.output.append(' $ ' + ''.join(__keyPressesIn[-1]).encode().decode().replace('\n', '↵'))
|
|
1231
1248
|
# host.stdout.append(' $ ' + ''.join(__keyPressesIn[-1]).encode().decode().replace('\n', '↵'))
|
|
1232
|
-
return
|
|
1249
|
+
return sentInputPos
|
|
1233
1250
|
|
|
1234
1251
|
def run_command(host, sem, timeout=60,passwds=None, retry_limit = 5):
|
|
1235
1252
|
'''
|
|
@@ -1382,7 +1399,7 @@ def run_command(host, sem, timeout=60,passwds=None, retry_limit = 5):
|
|
|
1382
1399
|
else:
|
|
1383
1400
|
fileArgs = host.files + [f'{host.resolvedName}:{host.command}']
|
|
1384
1401
|
if useScp:
|
|
1385
|
-
formatedCMD = [_binPaths['scp'],'-
|
|
1402
|
+
formatedCMD = [_binPaths['scp'],'-rp'] + localExtraArgs + extraargs +['--']+fileArgs
|
|
1386
1403
|
else:
|
|
1387
1404
|
formatedCMD = [_binPaths['rsync'],'-ahlX','--partial','--inplace', '--info=name'] + rsyncLocalExtraArgs + extraargs +['--']+fileArgs
|
|
1388
1405
|
else:
|
|
@@ -1415,11 +1432,11 @@ def run_command(host, sem, timeout=60,passwds=None, retry_limit = 5):
|
|
|
1415
1432
|
#host.stdout = []
|
|
1416
1433
|
proc = subprocess.Popen(formatedCMD,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE)
|
|
1417
1434
|
# create a thread to handle stdout
|
|
1418
|
-
stdout_thread = threading.Thread(target=__handle_reading_stream, args=(proc.stdout,host.stdout, host), daemon=True)
|
|
1435
|
+
stdout_thread = threading.Thread(target=__handle_reading_stream, args=(proc.stdout,host.stdout, host,host.stdout_buffer), daemon=True)
|
|
1419
1436
|
stdout_thread.start()
|
|
1420
1437
|
# create a thread to handle stderr
|
|
1421
1438
|
#host.stderr = []
|
|
1422
|
-
stderr_thread = threading.Thread(target=__handle_reading_stream, args=(proc.stderr,host.stderr, host), daemon=True)
|
|
1439
|
+
stderr_thread = threading.Thread(target=__handle_reading_stream, args=(proc.stderr,host.stderr, host,host.stderr_buffer), daemon=True)
|
|
1423
1440
|
stderr_thread.start()
|
|
1424
1441
|
# create a thread to handle stdin
|
|
1425
1442
|
stdin_stop_event = threading.Event()
|
|
@@ -1479,9 +1496,9 @@ def run_command(host, sem, timeout=60,passwds=None, retry_limit = 5):
|
|
|
1479
1496
|
except subprocess.TimeoutExpired:
|
|
1480
1497
|
pass
|
|
1481
1498
|
if stdout:
|
|
1482
|
-
__handle_reading_stream(io.BytesIO(stdout),host.stdout, host)
|
|
1499
|
+
__handle_reading_stream(io.BytesIO(stdout),host.stdout, host,host.stdout_buffer)
|
|
1483
1500
|
if stderr:
|
|
1484
|
-
__handle_reading_stream(io.BytesIO(stderr),host.stderr, host)
|
|
1501
|
+
__handle_reading_stream(io.BytesIO(stderr),host.stderr, host,host.stderr_buffer)
|
|
1485
1502
|
# if the last line in host.stderr is Connection to * closed., we will remove it
|
|
1486
1503
|
host.returncode = proc.poll()
|
|
1487
1504
|
if host.returncode is None:
|
|
@@ -2174,6 +2191,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
2174
2191
|
for i in range(host_window_height - 1):
|
|
2175
2192
|
_curses_add_string_to_window(window=host_window, color_pair_list=[-1, -1, 1], y=i + 1,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
|
|
2176
2193
|
host.lineNumToPrintSet.update(range(len(host.output)))
|
|
2194
|
+
host.lastPrintedUpdateTime = 0
|
|
2177
2195
|
# for i in range(host.printedLines, len(host.output)):
|
|
2178
2196
|
# _curses_add_string_to_window(window=host_window, y=i + 1, line=host.output[i], color_pair_list=host.current_color_pair,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
|
|
2179
2197
|
# host.printedLines = len(host.output)
|
|
@@ -2193,7 +2211,12 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
2193
2211
|
# print(traceback.format_exc().strip())
|
|
2194
2212
|
if org_dim != stdscr.getmaxyx():
|
|
2195
2213
|
return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal resize detected')
|
|
2214
|
+
if host.lastPrintedUpdateTime != host.lastUpdateTime and host.output_buffer.tell() > 0:
|
|
2215
|
+
# this means there is still output in the buffer, we will print it
|
|
2216
|
+
# we will print the output in the window
|
|
2217
|
+
_curses_add_string_to_window(window=host_window, y=len(host.output) + 1, line=host.output_buffer.getvalue().decode(_encoding,errors='backslashreplace'), color_pair_list=host.current_color_pair,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color,fill_char='')
|
|
2196
2218
|
host_window.noutrefresh()
|
|
2219
|
+
host.lastPrintedUpdateTime = host.lastUpdateTime
|
|
2197
2220
|
hosts_to_display, host_stats,rearrangedHosts = _get_hosts_to_display(hosts, max_num_hosts,hosts_to_display, indexOffset)
|
|
2198
2221
|
curses.doupdate()
|
|
2199
2222
|
last_refresh_time = time.perf_counter()
|
|
@@ -2420,7 +2443,7 @@ def processRunOnHosts(timeout, password, max_connections, hosts, returnUnfinishe
|
|
|
2420
2443
|
# check for the old content, only update if the new content is different
|
|
2421
2444
|
if not os.path.exists(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv')):
|
|
2422
2445
|
with open(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv'),'w') as f:
|
|
2423
|
-
f.writelines(f'{host},{expTime}' for host,expTime in unavailableHosts.
|
|
2446
|
+
f.writelines(f'{host},{expTime}' for host,expTime in unavailableHosts.items())
|
|
2424
2447
|
else:
|
|
2425
2448
|
oldDic = {}
|
|
2426
2449
|
try:
|
|
@@ -2545,7 +2568,7 @@ def getStrCommand(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAULT_ONE_O
|
|
|
2545
2568
|
history_file = history_file, env_file = env_file,
|
|
2546
2569
|
repeat = repeat,interval = interval,
|
|
2547
2570
|
shortend = shortend)
|
|
2548
|
-
commands = [command.replace('"', '\\"') for command in commands]
|
|
2571
|
+
commands = [command.replace('"', '\\"').replace('\n', '\\n').replace('\t', '\\t') for command in commands]
|
|
2549
2572
|
commandStr = '"' + '" "'.join(commands) + '"' if commands else ''
|
|
2550
2573
|
filePath = os.path.abspath(__file__)
|
|
2551
2574
|
programName = filePath if filePath else 'mssh'
|
|
@@ -2985,23 +3008,9 @@ def write_default_config(args,CONFIG_FILE = None):
|
|
|
2985
3008
|
eprint(f'Printing the config file to stdout:')
|
|
2986
3009
|
print(json.dumps(__configs_from_file, indent=4))
|
|
2987
3010
|
|
|
2988
|
-
#%% ------------
|
|
2989
|
-
def
|
|
2990
|
-
global _emo
|
|
2991
|
-
global __global_suppress_printout
|
|
2992
|
-
global __mainReturnCode
|
|
2993
|
-
global __failedHosts
|
|
2994
|
-
global __ipmiiInterfaceIPPrefix
|
|
3011
|
+
#%% ------------ Argument Processing -----------------
|
|
3012
|
+
def get_parser():
|
|
2995
3013
|
global _binPaths
|
|
2996
|
-
global _env_file
|
|
2997
|
-
global __DEBUG_MODE
|
|
2998
|
-
global __configs_from_file
|
|
2999
|
-
global _encoding
|
|
3000
|
-
global __returnZero
|
|
3001
|
-
_emo = False
|
|
3002
|
-
# We handle the signal
|
|
3003
|
-
signal.signal(signal.SIGINT, signal_handler)
|
|
3004
|
-
# We parse the arguments
|
|
3005
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}',
|
|
3006
3015
|
epilog=f'Found bins: {list(_binPaths.values())}\n Missing bins: {_binCalled - set(_binPaths.keys())}')
|
|
3007
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)
|
|
@@ -3052,18 +3061,20 @@ def main():
|
|
|
3052
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')
|
|
3053
3062
|
parser.add_argument('-e','--encoding', type=str, help=f'The encoding to use for the output. (default: {DEFAULT_ENCODING})', default=DEFAULT_ENCODING)
|
|
3054
3063
|
parser.add_argument("-V","--version", action='version', version=f'%(prog)s {version} @ {COMMIT_DATE} with [ {", ".join(_binPaths.keys())} ] by {AUTHOR} ({AUTHOR_EMAIL})')
|
|
3055
|
-
|
|
3056
|
-
# parser.add_argument('-u', '--user', metavar='user', type=str, nargs=1,
|
|
3057
|
-
# help='the user to use to connect to the hosts')
|
|
3058
|
-
#args = parser.parse_args()
|
|
3064
|
+
return parser
|
|
3059
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
|
|
3060
3071
|
# if python version is 3.7 or higher, use parse_intermixed_args
|
|
3061
3072
|
try:
|
|
3062
|
-
args = parser.parse_intermixed_args()
|
|
3073
|
+
args = parser.parse_intermixed_args(args)
|
|
3063
3074
|
except Exception :
|
|
3064
3075
|
#eprint(f"Error while parsing arguments: {e!r}")
|
|
3065
3076
|
# try to parse the arguments using parse_known_args
|
|
3066
|
-
args, unknown = parser.parse_known_args()
|
|
3077
|
+
args, unknown = parser.parse_known_args(args)
|
|
3067
3078
|
# if there are unknown arguments, we will try to parse them again using parse_args
|
|
3068
3079
|
if unknown:
|
|
3069
3080
|
eprint(f"Warning: Unknown arguments, treating all as commands: {unknown!r}")
|
|
@@ -3076,10 +3087,14 @@ def main():
|
|
|
3076
3087
|
args.no_history = True
|
|
3077
3088
|
args.greppable = True
|
|
3078
3089
|
args.error_only = True
|
|
3079
|
-
|
|
3080
|
-
if args.return_zero:
|
|
3081
|
-
__returnZero = True
|
|
3082
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
|
|
3083
3098
|
if args.generate_config_file or args.store_config_file:
|
|
3084
3099
|
if args.store_config_file:
|
|
3085
3100
|
configFileToWriteTo = args.store_config_file
|
|
@@ -3101,11 +3116,12 @@ def main():
|
|
|
3101
3116
|
__configs_from_file.update(load_config_file(os.path.expanduser(args.config_file)))
|
|
3102
3117
|
else:
|
|
3103
3118
|
eprint(f"Warning: Config file {args.config_file!r} not found, ignoring it.")
|
|
3119
|
+
return args
|
|
3104
3120
|
|
|
3105
|
-
_env_file = args.env_file
|
|
3106
|
-
__DEBUG_MODE = args.debug
|
|
3107
3121
|
# if there are more than 1 commands, and every command only consists of one word,
|
|
3108
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):
|
|
3109
3125
|
if not args.file and len(args.commands) > 1 and all([len(command.split()) == 1 for command in args.commands]):
|
|
3110
3126
|
eprint(f"Multiple one word command detected, what to do? (1/m/n)")
|
|
3111
3127
|
eprint(f"1: Run 1 command [{' '.join(args.commands)}] on all hosts ( default )")
|
|
@@ -3119,7 +3135,9 @@ def main():
|
|
|
3119
3135
|
eprint(f"\nRunning multiple commands: {', '.join(args.commands)!r} on all hosts")
|
|
3120
3136
|
else:
|
|
3121
3137
|
_exit_with_code(0, "Aborted by user, no commands to run")
|
|
3138
|
+
return args
|
|
3122
3139
|
|
|
3140
|
+
def process_keys(args):
|
|
3123
3141
|
if args.key or args.use_key:
|
|
3124
3142
|
if not args.key:
|
|
3125
3143
|
args.key = find_ssh_key_file()
|
|
@@ -3128,23 +3146,43 @@ def main():
|
|
|
3128
3146
|
args.key = find_ssh_key_file(args.key)
|
|
3129
3147
|
elif not os.path.exists(args.key):
|
|
3130
3148
|
eprint(f"Warning: Identity file {args.key!r} not found. Passing to ssh anyway. Proceed with caution.")
|
|
3149
|
+
return args
|
|
3150
|
+
|
|
3131
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
|
|
3132
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
|
|
3133
3167
|
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
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)
|
|
3140
3178
|
|
|
3141
3179
|
if args.use_script_timeout:
|
|
3142
3180
|
# set timeout to the default script timeout if timeout is not set
|
|
3143
3181
|
if args.timeout == DEFAULT_CLI_TIMEOUT:
|
|
3144
3182
|
args.timeout = DEFAULT_TIMEOUT
|
|
3145
3183
|
|
|
3146
|
-
|
|
3147
|
-
|
|
3184
|
+
if args.no_output:
|
|
3185
|
+
__global_suppress_printout = True
|
|
3148
3186
|
if not __global_suppress_printout:
|
|
3149
3187
|
cmdStr = getStrCommand(args.hosts,args.commands,
|
|
3150
3188
|
oneonone=args.oneonone,timeout=args.timeout,password=args.password,
|
|
@@ -3176,9 +3214,7 @@ def main():
|
|
|
3176
3214
|
history_file = args.history_file,
|
|
3177
3215
|
)
|
|
3178
3216
|
#print('*'*80)
|
|
3179
|
-
|
|
3180
3217
|
#if not __global_suppress_printout: eprint('-'*80)
|
|
3181
|
-
|
|
3182
3218
|
succeededHosts = set()
|
|
3183
3219
|
for host in hosts:
|
|
3184
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.77.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
multiSSH3.py,sha256=NzwlKjLdj7y3o3mhe28Ks80SS8E-FFNsp9UPQNNACTE,150917
|
|
2
|
-
multissh3-5.77.dist-info/METADATA,sha256=1kG-DHm3Bek62gr67O8QsAmOeLNBbd-D6ZjeDl9rItw,18093
|
|
3
|
-
multissh3-5.77.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
-
multissh3-5.77.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
-
multissh3-5.77.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
-
multissh3-5.77.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|