multiSSH3 5.40__tar.gz → 5.41__tar.gz

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: multiSSH3
3
- Version: 5.40
3
+ Version: 5.41
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: multiSSH3
3
- Version: 5.40
3
+ Version: 5.41
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
@@ -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.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 - 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:
@@ -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 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
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:
File without changes
File without changes
File without changes
File without changes