multiSSH3 5.40__py3-none-any.whl → 5.41__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.40.dist-info → multiSSH3-5.41.dist-info}/METADATA +1 -1
- multiSSH3-5.41.dist-info/RECORD +7 -0
- multiSSH3.py +34 -10
- multiSSH3-5.40.dist-info/RECORD +0 -7
- {multiSSH3-5.40.dist-info → multiSSH3-5.41.dist-info}/LICENSE +0 -0
- {multiSSH3-5.40.dist-info → multiSSH3-5.41.dist-info}/WHEEL +0 -0
- {multiSSH3-5.40.dist-info → multiSSH3-5.41.dist-info}/entry_points.txt +0 -0
- {multiSSH3-5.40.dist-info → multiSSH3-5.41.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
multiSSH3.py,sha256=r2YsjfXdlHrhm6umuKOiVFtZiuzFt3rnoI6rRmf6hs4,138635
|
|
2
|
+
multiSSH3-5.41.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
3
|
+
multiSSH3-5.41.dist-info/METADATA,sha256=MQgaTixPbZ-NiRgxZBrq9_1PtmkvqpWUBA8T93rlx1s,18366
|
|
4
|
+
multiSSH3-5.41.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
5
|
+
multiSSH3-5.41.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
6
|
+
multiSSH3-5.41.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
7
|
+
multiSSH3-5.41.dist-info/RECORD,,
|
multiSSH3.py
CHANGED
|
@@ -45,7 +45,7 @@ except AttributeError:
|
|
|
45
45
|
# If neither is available, use a dummy decorator
|
|
46
46
|
def cache_decorator(func):
|
|
47
47
|
return func
|
|
48
|
-
version = '5.
|
|
48
|
+
version = '5.41'
|
|
49
49
|
VERSION = version
|
|
50
50
|
|
|
51
51
|
CONFIG_FILE = '/etc/multiSSH3.config.json'
|
|
@@ -361,12 +361,13 @@ if True:
|
|
|
361
361
|
__curses_color_table = {}
|
|
362
362
|
__curses_current_color_index = 10
|
|
363
363
|
__max_connections_nofile_limit_supported = 0
|
|
364
|
+
__thread_start_delay = 0
|
|
364
365
|
if __resource_lib_available:
|
|
365
366
|
# Get the current limits
|
|
366
367
|
_, __system_nofile_limit = resource.getrlimit(resource.RLIMIT_NOFILE)
|
|
367
368
|
# Set the soft limit to the hard limit
|
|
368
369
|
resource.setrlimit(resource.RLIMIT_NOFILE, (__system_nofile_limit, __system_nofile_limit))
|
|
369
|
-
__max_connections_nofile_limit_supported = int((__system_nofile_limit -
|
|
370
|
+
__max_connections_nofile_limit_supported = int((__system_nofile_limit - 10) / 3)
|
|
370
371
|
|
|
371
372
|
# Mapping of ANSI 4-bit colors to curses colors
|
|
372
373
|
if __curses_available:
|
|
@@ -1166,7 +1167,7 @@ def __handle_writing_stream(stream,stop_event,host):
|
|
|
1166
1167
|
# host.stdout.append(' $ ' + ''.join(__keyPressesIn[-1]).encode().decode().replace('\n', '↵'))
|
|
1167
1168
|
return sentInput
|
|
1168
1169
|
|
|
1169
|
-
def run_command(host, sem, timeout=60,passwds=None):
|
|
1170
|
+
def run_command(host, sem, timeout=60,passwds=None, retry_limit = 5):
|
|
1170
1171
|
'''
|
|
1171
1172
|
Run the command on the host. Will format the commands accordingly. Main execution function.
|
|
1172
1173
|
|
|
@@ -1184,6 +1185,11 @@ def run_command(host, sem, timeout=60,passwds=None):
|
|
|
1184
1185
|
global __ipmiiInterfaceIPPrefix
|
|
1185
1186
|
global _binPaths
|
|
1186
1187
|
global __DEBUG_MODE
|
|
1188
|
+
if retry_limit < 0:
|
|
1189
|
+
host.output.append('Error: Retry limit reached!')
|
|
1190
|
+
host.stderr.append('Error: Retry limit reached!')
|
|
1191
|
+
host.returncode = 1
|
|
1192
|
+
return
|
|
1187
1193
|
try:
|
|
1188
1194
|
localExtraArgs = []
|
|
1189
1195
|
|
|
@@ -1260,7 +1266,7 @@ def run_command(host, sem, timeout=60,passwds=None):
|
|
|
1260
1266
|
host.command = 'ipmitool power status'
|
|
1261
1267
|
else:
|
|
1262
1268
|
host.command = 'ipmitool '+host.command if not host.command.startswith('ipmitool ') else host.command
|
|
1263
|
-
run_command(host,sem,timeout,passwds)
|
|
1269
|
+
run_command(host,sem,timeout,passwds,retry_limit=retry_limit - 1)
|
|
1264
1270
|
return
|
|
1265
1271
|
else:
|
|
1266
1272
|
host.output.append('Ipmitool not found on the local machine! Please install ipmitool to use ipmi mode.')
|
|
@@ -1279,7 +1285,7 @@ def run_command(host, sem, timeout=60,passwds=None):
|
|
|
1279
1285
|
host.stderr.append('shell not found on the local machine! Using ssh localhost instead...')
|
|
1280
1286
|
host.shell = False
|
|
1281
1287
|
host.name = 'localhost'
|
|
1282
|
-
run_command(host,sem,timeout,passwds)
|
|
1288
|
+
run_command(host,sem,timeout,passwds,retry_limit=retry_limit - 1)
|
|
1283
1289
|
else:
|
|
1284
1290
|
if host.files:
|
|
1285
1291
|
if host.scp:
|
|
@@ -1420,6 +1426,16 @@ def run_command(host, sem, timeout=60,passwds=None):
|
|
|
1420
1426
|
if host.stderr:
|
|
1421
1427
|
# filter out the error messages that we want to ignore
|
|
1422
1428
|
host.stderr = [line for line in host.stderr if not __ERROR_MESSAGES_TO_IGNORE_REGEX.search(line)]
|
|
1429
|
+
# except os error too many open files
|
|
1430
|
+
except OSError as e:
|
|
1431
|
+
if e.errno == 24: # Errno 24 corresponds to "Too many open files"
|
|
1432
|
+
host.output.append("Warning: Too many open files. retrying...")
|
|
1433
|
+
# Handle the error, e.g., clean up, retry logic, or exit
|
|
1434
|
+
time.sleep(0.1)
|
|
1435
|
+
run_command(host,sem,timeout,passwds,retry_limit=retry_limit - 1)
|
|
1436
|
+
else:
|
|
1437
|
+
# Re-raise the exception if it's not the specific one
|
|
1438
|
+
raise
|
|
1423
1439
|
except Exception as e:
|
|
1424
1440
|
import traceback
|
|
1425
1441
|
host.stderr.extend(str(e).split('\n'))
|
|
@@ -1436,7 +1452,7 @@ def run_command(host, sem, timeout=60,passwds=None):
|
|
|
1436
1452
|
host.ipmi = False
|
|
1437
1453
|
host.interface_ip_prefix = None
|
|
1438
1454
|
host.command = 'ipmitool '+host.command if not host.command.startswith('ipmitool ') else host.command
|
|
1439
|
-
run_command(host,sem,timeout,passwds)
|
|
1455
|
+
run_command(host,sem,timeout,passwds,retry_limit=retry_limit - 1)
|
|
1440
1456
|
# If transfering files, we will try again using scp if rsync connection is not successful
|
|
1441
1457
|
if host.files and not host.scp and not useScp and host.returncode != 0 and host.stderr:
|
|
1442
1458
|
host.stderr = []
|
|
@@ -1445,7 +1461,7 @@ def run_command(host, sem, timeout=60,passwds=None):
|
|
|
1445
1461
|
if __DEBUG_MODE:
|
|
1446
1462
|
host.stderr.append('Rsync connection failed! Trying SCP connection...')
|
|
1447
1463
|
host.scp = True
|
|
1448
|
-
run_command(host,sem,timeout,passwds)
|
|
1464
|
+
run_command(host,sem,timeout,passwds,retry_limit=retry_limit - 1)
|
|
1449
1465
|
|
|
1450
1466
|
# ------------ Start Threading Block ----------------
|
|
1451
1467
|
def start_run_on_hosts(hosts, timeout=60,password=None,max_connections=4 * os.cpu_count()):
|
|
@@ -1461,12 +1477,14 @@ def start_run_on_hosts(hosts, timeout=60,password=None,max_connections=4 * os.cp
|
|
|
1461
1477
|
Returns:
|
|
1462
1478
|
list: A list of threads that get started
|
|
1463
1479
|
'''
|
|
1480
|
+
global __thread_start_delay
|
|
1464
1481
|
if len(hosts) == 0:
|
|
1465
1482
|
return []
|
|
1466
1483
|
sem = threading.Semaphore(max_connections) # Limit concurrent SSH sessions
|
|
1467
1484
|
threads = [threading.Thread(target=run_command, args=(host, sem,timeout,password), daemon=True) for host in hosts]
|
|
1468
1485
|
for thread in threads:
|
|
1469
1486
|
thread.start()
|
|
1487
|
+
time.sleep(__thread_start_delay)
|
|
1470
1488
|
return threads
|
|
1471
1489
|
|
|
1472
1490
|
# ------------ Display Block ----------------
|
|
@@ -2421,6 +2439,8 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2421
2439
|
global _no_env
|
|
2422
2440
|
global _emo
|
|
2423
2441
|
global __DEBUG_MODE
|
|
2442
|
+
global __thread_start_delay
|
|
2443
|
+
global __max_connections_nofile_limit_supported
|
|
2424
2444
|
_emo = False
|
|
2425
2445
|
_no_env = no_env
|
|
2426
2446
|
if os.path.exists(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')):
|
|
@@ -2456,9 +2476,13 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2456
2476
|
max_connections = __max_connections_nofile_limit_supported
|
|
2457
2477
|
elif max_connections < 0:
|
|
2458
2478
|
max_connections = (-max_connections) * os.cpu_count()
|
|
2459
|
-
if __max_connections_nofile_limit_supported > 0
|
|
2460
|
-
|
|
2461
|
-
|
|
2479
|
+
if __max_connections_nofile_limit_supported > 0:
|
|
2480
|
+
if max_connections > __max_connections_nofile_limit_supported:
|
|
2481
|
+
eprint(f"Warning: The number of maximum connections {max_connections} is larger than estimated limit {__max_connections_nofile_limit_supported} from ulimit nofile limit {__system_nofile_limit}, setting the maximum connections to {__max_connections_nofile_limit_supported}.")
|
|
2482
|
+
max_connections = __max_connections_nofile_limit_supported
|
|
2483
|
+
if max_connections > __max_connections_nofile_limit_supported * 2:
|
|
2484
|
+
# we need to throttle thread start to avoid hitting the nofile limit
|
|
2485
|
+
__thread_start_delay = 0.001
|
|
2462
2486
|
if not commands:
|
|
2463
2487
|
commands = []
|
|
2464
2488
|
else:
|
multiSSH3-5.40.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
multiSSH3.py,sha256=X9lJZNiWAuiNtEnSjBkqSgbgGMWhxB5MZIjS_ejpj14,137577
|
|
2
|
-
multiSSH3-5.40.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
3
|
-
multiSSH3-5.40.dist-info/METADATA,sha256=pJGmT8HeakwctZYc4uW54_Z-v2Q1liCL1TjD7xddV5E,18366
|
|
4
|
-
multiSSH3-5.40.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
5
|
-
multiSSH3-5.40.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
6
|
-
multiSSH3-5.40.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
7
|
-
multiSSH3-5.40.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|