multiSSH3 5.40__py3-none-any.whl → 5.42__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.

@@ -0,0 +1,6 @@
1
+ multiSSH3.py,sha256=Xs05pxUY2WXSF-o98K87wKbIwNxtkKZlDwrthtzQf1Q,138824
2
+ multiSSH3-5.42.dist-info/METADATA,sha256=xtXbj1Q5ezo_UxPdqOp9b2vcBnkywdQY1gHeTwAARYQ,18001
3
+ multiSSH3-5.42.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
4
+ multiSSH3-5.42.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
5
+ multiSSH3-5.42.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
6
+ multiSSH3-5.42.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.40'
48
+ version = '5.42'
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 - 5) / 4.6)
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:
@@ -1155,7 +1156,7 @@ def __handle_writing_stream(stream,stop_event,host):
1155
1156
  sentInput += 1
1156
1157
  host.lastUpdateTime = time.time()
1157
1158
  else:
1158
- time.sleep(0.1)
1159
+ time.sleep(0.01) # sleep for 10ms
1159
1160
  if sentInput < len(__keyPressesIn) - 1 :
1160
1161
  eprint(f"Warning: {len(__keyPressesIn)-sentInput} key presses are not sent before the process is terminated!")
1161
1162
  # # send the last line
@@ -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:
@@ -1358,6 +1364,7 @@ def run_command(host, sem, timeout=60,passwds=None):
1358
1364
  # Monitor the subprocess and terminate it after the timeout
1359
1365
  host.lastUpdateTime = time.time()
1360
1366
  timeoutLineAppended = False
1367
+ sleep_interval = 1.0e-8 # 10 nanoseconds
1361
1368
  while proc.poll() is None: # while the process is still running
1362
1369
  if timeout > 0:
1363
1370
  if time.time() - host.lastUpdateTime > timeout:
@@ -1365,7 +1372,6 @@ def run_command(host, sem, timeout=60,passwds=None):
1365
1372
  host.output.append('Timeout!')
1366
1373
  proc.send_signal(signal.SIGINT)
1367
1374
  time.sleep(0.1)
1368
-
1369
1375
  proc.terminate()
1370
1376
  break
1371
1377
  elif time.time() - host.lastUpdateTime > max(1, timeout // 2):
@@ -1389,7 +1395,11 @@ def run_command(host, sem, timeout=60,passwds=None):
1389
1395
  time.sleep(0.1)
1390
1396
  proc.terminate()
1391
1397
  break
1392
- time.sleep(0.1) # avoid busy-waiting
1398
+ time.sleep(sleep_interval) # avoid busy-waiting
1399
+ if sleep_interval < 0.001:
1400
+ sleep_interval *= 2
1401
+ elif sleep_interval < 0.01:
1402
+ sleep_interval *= 1.1
1393
1403
  stdin_stop_event.set()
1394
1404
  # Wait for output processing to complete
1395
1405
  stdout_thread.join(timeout=1)
@@ -1420,6 +1430,16 @@ def run_command(host, sem, timeout=60,passwds=None):
1420
1430
  if host.stderr:
1421
1431
  # filter out the error messages that we want to ignore
1422
1432
  host.stderr = [line for line in host.stderr if not __ERROR_MESSAGES_TO_IGNORE_REGEX.search(line)]
1433
+ # except os error too many open files
1434
+ except OSError as e:
1435
+ if e.errno == 24: # Errno 24 corresponds to "Too many open files"
1436
+ host.output.append("Warning: Too many open files. retrying...")
1437
+ # Handle the error, e.g., clean up, retry logic, or exit
1438
+ time.sleep(0.1)
1439
+ run_command(host,sem,timeout,passwds,retry_limit=retry_limit - 1)
1440
+ else:
1441
+ # Re-raise the exception if it's not the specific one
1442
+ raise
1423
1443
  except Exception as e:
1424
1444
  import traceback
1425
1445
  host.stderr.extend(str(e).split('\n'))
@@ -1436,7 +1456,7 @@ def run_command(host, sem, timeout=60,passwds=None):
1436
1456
  host.ipmi = False
1437
1457
  host.interface_ip_prefix = None
1438
1458
  host.command = 'ipmitool '+host.command if not host.command.startswith('ipmitool ') else host.command
1439
- run_command(host,sem,timeout,passwds)
1459
+ run_command(host,sem,timeout,passwds,retry_limit=retry_limit - 1)
1440
1460
  # If transfering files, we will try again using scp if rsync connection is not successful
1441
1461
  if host.files and not host.scp and not useScp and host.returncode != 0 and host.stderr:
1442
1462
  host.stderr = []
@@ -1445,7 +1465,7 @@ def run_command(host, sem, timeout=60,passwds=None):
1445
1465
  if __DEBUG_MODE:
1446
1466
  host.stderr.append('Rsync connection failed! Trying SCP connection...')
1447
1467
  host.scp = True
1448
- run_command(host,sem,timeout,passwds)
1468
+ run_command(host,sem,timeout,passwds,retry_limit=retry_limit - 1)
1449
1469
 
1450
1470
  # ------------ Start Threading Block ----------------
1451
1471
  def start_run_on_hosts(hosts, timeout=60,password=None,max_connections=4 * os.cpu_count()):
@@ -1461,12 +1481,14 @@ def start_run_on_hosts(hosts, timeout=60,password=None,max_connections=4 * os.cp
1461
1481
  Returns:
1462
1482
  list: A list of threads that get started
1463
1483
  '''
1484
+ global __thread_start_delay
1464
1485
  if len(hosts) == 0:
1465
1486
  return []
1466
1487
  sem = threading.Semaphore(max_connections) # Limit concurrent SSH sessions
1467
1488
  threads = [threading.Thread(target=run_command, args=(host, sem,timeout,password), daemon=True) for host in hosts]
1468
1489
  for thread in threads:
1469
1490
  thread.start()
1491
+ time.sleep(__thread_start_delay)
1470
1492
  return threads
1471
1493
 
1472
1494
  # ------------ Display Block ----------------
@@ -2240,7 +2262,7 @@ def processRunOnHosts(timeout, password, max_connections, hosts, returnUnfinishe
2240
2262
  if not returnUnfinished:
2241
2263
  # wait until all hosts have a return code
2242
2264
  while any([host.returncode is None for host in hosts]):
2243
- time.sleep(0.1)
2265
+ time.sleep(0.01)
2244
2266
  for thread in threads:
2245
2267
  thread.join(timeout=3)
2246
2268
  # update the unavailable hosts and global unavailable hosts
@@ -2421,6 +2443,8 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2421
2443
  global _no_env
2422
2444
  global _emo
2423
2445
  global __DEBUG_MODE
2446
+ global __thread_start_delay
2447
+ global __max_connections_nofile_limit_supported
2424
2448
  _emo = False
2425
2449
  _no_env = no_env
2426
2450
  if os.path.exists(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')):
@@ -2456,9 +2480,13 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2456
2480
  max_connections = __max_connections_nofile_limit_supported
2457
2481
  elif max_connections < 0:
2458
2482
  max_connections = (-max_connections) * os.cpu_count()
2459
- if __max_connections_nofile_limit_supported > 0 and max_connections > __max_connections_nofile_limit_supported:
2460
- 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}.")
2461
- max_connections = __max_connections_nofile_limit_supported
2483
+ if __max_connections_nofile_limit_supported > 0:
2484
+ if max_connections > __max_connections_nofile_limit_supported:
2485
+ 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}.")
2486
+ max_connections = __max_connections_nofile_limit_supported
2487
+ if max_connections > __max_connections_nofile_limit_supported * 2:
2488
+ # we need to throttle thread start to avoid hitting the nofile limit
2489
+ __thread_start_delay = 0.001
2462
2490
  if not commands:
2463
2491
  commands = []
2464
2492
  else: