multiSSH3 5.47__py3-none-any.whl → 5.50__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-5.47.dist-info → multiSSH3-5.50.dist-info}/METADATA +3 -1
- multiSSH3-5.50.dist-info/RECORD +6 -0
- multiSSH3.py +78 -32
- multiSSH3-5.47.dist-info/RECORD +0 -6
- {multiSSH3-5.47.dist-info → multiSSH3-5.50.dist-info}/WHEEL +0 -0
- {multiSSH3-5.47.dist-info → multiSSH3-5.50.dist-info}/entry_points.txt +0 -0
- {multiSSH3-5.47.dist-info → multiSSH3-5.50.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: multiSSH3
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.50
|
|
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
|
|
@@ -9,6 +9,8 @@ License: GPLv3+
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
11
|
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
12
14
|
Requires-Python: >=3.6
|
|
13
15
|
Description-Content-Type: text/markdown
|
|
14
16
|
Requires-Dist: argparse
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
multiSSH3.py,sha256=4XPEcfGf7Gqq0YvVvsAve4x5Afb4Nse_0_nRJTm5T6A,137371
|
|
2
|
+
multiSSH3-5.50.dist-info/METADATA,sha256=63ZfVMNdX55SxVFWL8EogyLiOHWDerdjsVovEbNCBf8,18092
|
|
3
|
+
multiSSH3-5.50.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
4
|
+
multiSSH3-5.50.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
+
multiSSH3-5.50.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
+
multiSSH3-5.50.dist-info/RECORD,,
|
multiSSH3.py
CHANGED
|
@@ -34,6 +34,7 @@ import uuid
|
|
|
34
34
|
import tempfile
|
|
35
35
|
import math
|
|
36
36
|
from itertools import count
|
|
37
|
+
import queue
|
|
37
38
|
|
|
38
39
|
try:
|
|
39
40
|
# Check if functiools.cache is available
|
|
@@ -46,8 +47,9 @@ except AttributeError:
|
|
|
46
47
|
# If neither is available, use a dummy decorator
|
|
47
48
|
def cache_decorator(func):
|
|
48
49
|
return func
|
|
49
|
-
version = '5.
|
|
50
|
+
version = '5.50'
|
|
50
51
|
VERSION = version
|
|
52
|
+
__version__ = version
|
|
51
53
|
COMMIT_DATE = '2025-01-30'
|
|
52
54
|
|
|
53
55
|
CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
|
|
@@ -90,31 +92,68 @@ def signal_handler(sig, frame):
|
|
|
90
92
|
os.system(f'pkill -ef {os.path.basename(__file__)}')
|
|
91
93
|
sys.exit(0)
|
|
92
94
|
|
|
95
|
+
# def input_with_timeout_and_countdown(timeout, prompt='Please enter your selection'):
|
|
96
|
+
# """
|
|
97
|
+
# Read an input from the user with a timeout and a countdown.
|
|
98
|
+
|
|
99
|
+
# Parameters:
|
|
100
|
+
# timeout (int): The timeout value in seconds.
|
|
101
|
+
# prompt (str): The prompt message to display to the user. Default is 'Please enter your selection'.
|
|
102
|
+
|
|
103
|
+
# Returns:
|
|
104
|
+
# str or None: The user input if received within the timeout, or None if no input is received.
|
|
105
|
+
# """
|
|
106
|
+
# import select
|
|
107
|
+
# # Print the initial prompt with the countdown
|
|
108
|
+
# eprint(f"{prompt} [{timeout}s]: ", end='', flush=True)
|
|
109
|
+
# # Loop until the timeout
|
|
110
|
+
# for remaining in range(timeout, 0, -1):
|
|
111
|
+
# # If there is an input, return it
|
|
112
|
+
# # this only works on linux
|
|
113
|
+
# if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
|
|
114
|
+
# return input().strip()
|
|
115
|
+
# # Print the remaining time
|
|
116
|
+
# eprint(f"\r{prompt} [{remaining}s]: ", end='', flush=True)
|
|
117
|
+
# # Wait a second
|
|
118
|
+
# time.sleep(1)
|
|
119
|
+
# # If there is no input, return None
|
|
120
|
+
# return None
|
|
121
|
+
|
|
93
122
|
def input_with_timeout_and_countdown(timeout, prompt='Please enter your selection'):
|
|
94
123
|
"""
|
|
95
|
-
Read
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
timeout (int): The timeout value in seconds.
|
|
99
|
-
prompt (str): The prompt message to display to the user. Default is 'Please enter your selection'.
|
|
100
|
-
|
|
101
|
-
Returns:
|
|
102
|
-
str or None: The user input if received within the timeout, or None if no input is received.
|
|
124
|
+
Read input from the user with a timeout (cross-platform).
|
|
125
|
+
If the user does not enter any input within `timeout` seconds, return None.
|
|
126
|
+
Otherwise, return the input string.
|
|
103
127
|
"""
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
# Queue to receive user input from the background thread
|
|
129
|
+
input_queue = queue.Queue()
|
|
130
|
+
def read_input():
|
|
131
|
+
# Read line from stdin and put it in the queue
|
|
132
|
+
user_input = sys.stdin.readline()
|
|
133
|
+
input_queue.put(user_input)
|
|
134
|
+
# Start a thread that will block on input()
|
|
135
|
+
input_thread = threading.Thread(target=read_input, daemon=True)
|
|
136
|
+
input_thread.start()
|
|
137
|
+
# Print the initial prompt
|
|
106
138
|
eprint(f"{prompt} [{timeout}s]: ", end='', flush=True)
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if
|
|
111
|
-
|
|
112
|
-
|
|
139
|
+
# Countdown loop
|
|
140
|
+
start_time = time.time()
|
|
141
|
+
while True:
|
|
142
|
+
# Check if the input thread has finished (i.e., user pressed Enter)
|
|
143
|
+
if not input_queue.empty():
|
|
144
|
+
# We got user input
|
|
145
|
+
user_input = input_queue.get().strip()
|
|
146
|
+
eprint() # move to the next line
|
|
147
|
+
return user_input
|
|
148
|
+
elapsed = int(time.time() - start_time)
|
|
149
|
+
remaining = timeout - elapsed
|
|
150
|
+
if remaining <= 0:
|
|
151
|
+
# Time is up, no input
|
|
152
|
+
eprint() # move to the next line
|
|
153
|
+
return None
|
|
154
|
+
# Update prompt countdown
|
|
113
155
|
eprint(f"\r{prompt} [{remaining}s]: ", end='', flush=True)
|
|
114
|
-
# Wait a second
|
|
115
156
|
time.sleep(1)
|
|
116
|
-
# If there is no input, return None
|
|
117
|
-
return None
|
|
118
157
|
|
|
119
158
|
@cache_decorator
|
|
120
159
|
def getIP(hostname: str,local=False):
|
|
@@ -2137,18 +2176,7 @@ def curses_print(stdscr, hosts, threads, min_char_len = DEFAULT_CURSES_MINIMUM_C
|
|
|
2137
2176
|
#time.sleep(0.25)
|
|
2138
2177
|
|
|
2139
2178
|
# ------------ Generate Output Block ----------------
|
|
2140
|
-
def
|
|
2141
|
-
'''
|
|
2142
|
-
Print / generate the output of the hosts to the terminal
|
|
2143
|
-
|
|
2144
|
-
Args:
|
|
2145
|
-
hosts (list): A list of Host objects
|
|
2146
|
-
usejson (bool, optional): Whether to print the output in JSON format. Defaults to False.
|
|
2147
|
-
quiet (bool, optional): Whether to print the output. Defaults to False.
|
|
2148
|
-
|
|
2149
|
-
Returns:
|
|
2150
|
-
str: The pretty output generated
|
|
2151
|
-
'''
|
|
2179
|
+
def generate_output(hosts, usejson = False, greppable = False):
|
|
2152
2180
|
global __keyPressesIn
|
|
2153
2181
|
global __global_suppress_printout
|
|
2154
2182
|
hosts = [dict(host) for host in hosts]
|
|
@@ -2213,6 +2241,21 @@ def print_output(hosts,usejson = False,quiet = False,greppable = False):
|
|
|
2213
2241
|
__keyPressesIn = [[]]
|
|
2214
2242
|
if __global_suppress_printout and not outputs:
|
|
2215
2243
|
rtnStr += 'Success'
|
|
2244
|
+
return rtnStr
|
|
2245
|
+
|
|
2246
|
+
def print_output(hosts,usejson = False,quiet = False,greppable = False):
|
|
2247
|
+
'''
|
|
2248
|
+
Print / generate the output of the hosts to the terminal
|
|
2249
|
+
|
|
2250
|
+
Args:
|
|
2251
|
+
hosts (list): A list of Host objects
|
|
2252
|
+
usejson (bool, optional): Whether to print the output in JSON format. Defaults to False.
|
|
2253
|
+
quiet (bool, optional): Whether to print the output. Defaults to False.
|
|
2254
|
+
|
|
2255
|
+
Returns:
|
|
2256
|
+
str: The pretty output generated
|
|
2257
|
+
'''
|
|
2258
|
+
rtnStr = generate_output(hosts,usejson,greppable)
|
|
2216
2259
|
if not quiet:
|
|
2217
2260
|
print(rtnStr)
|
|
2218
2261
|
return rtnStr
|
|
@@ -2410,6 +2453,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2410
2453
|
global __DEBUG_MODE
|
|
2411
2454
|
global __thread_start_delay
|
|
2412
2455
|
global __max_connections_nofile_limit_supported
|
|
2456
|
+
global __keyPressesIn
|
|
2413
2457
|
_emo = False
|
|
2414
2458
|
_no_env = no_env
|
|
2415
2459
|
if os.path.exists(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')):
|
|
@@ -2473,6 +2517,8 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2473
2517
|
unavailableHosts = __globalUnavailableHosts
|
|
2474
2518
|
else:
|
|
2475
2519
|
unavailableHosts = set()
|
|
2520
|
+
# set global input to empty
|
|
2521
|
+
__keyPressesIn = [[]]
|
|
2476
2522
|
else:
|
|
2477
2523
|
# if run in command line ( or emulating running in command line, we default to skip unreachable hosts within one command call )
|
|
2478
2524
|
if skipUnreachable:
|
multiSSH3-5.47.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
multiSSH3.py,sha256=fLoImfnt8GQDs9kLWsySLcFFcB7yXyprRMfntEz_-jk,135786
|
|
2
|
-
multiSSH3-5.47.dist-info/METADATA,sha256=b9DlbOVp-wi3Lu7nU5MuVcSk6CFZpAcdBDV02N_e328,18001
|
|
3
|
-
multiSSH3-5.47.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
4
|
-
multiSSH3-5.47.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
-
multiSSH3-5.47.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
-
multiSSH3-5.47.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|