multiSSH3 5.68__py3-none-any.whl → 5.69__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 CHANGED
@@ -54,7 +54,7 @@ except AttributeError:
54
54
  # If neither is available, use a dummy decorator
55
55
  def cache_decorator(func):
56
56
  return func
57
- version = '5.68'
57
+ version = '5.69'
58
58
  VERSION = version
59
59
  __version__ = version
60
60
  COMMIT_DATE = '2025-05-09'
@@ -349,7 +349,7 @@ __failedHosts = set()
349
349
  __wildCharacters = ['*','?','x']
350
350
  _no_env = DEFAULT_NO_ENV
351
351
  _env_file = DEFAULT_ENV_FILE
352
- __globalUnavailableHosts = set()
352
+ __globalUnavailableHosts = dict()
353
353
  __ipmiiInterfaceIPPrefix = DEFAULT_IPMI_INTERFACE_IP_PREFIX
354
354
  __keyPressesIn = [[]]
355
355
  _emo = False
@@ -2235,7 +2235,7 @@ def generate_output(hosts, usejson = False, greppable = False):
2235
2235
  hostPrintOut = f" Command:\n {host['command']}\n"
2236
2236
  hostPrintOut += " stdout:\n "+'\n '.join(host['stdout'])
2237
2237
  if host['stderr']:
2238
- if host['stderr'][0].strip().startswith('ssh: connect to host '):
2238
+ if host['stderr'][0].strip().startswith('ssh: connect to host ') and host['stderr'][0].strip().endswith('Connection refused'):
2239
2239
  host['stderr'][0] = 'SSH not reachable!'
2240
2240
  elif host['stderr'][-1].strip().endswith('Connection timed out'):
2241
2241
  host['stderr'][-1] = 'SSH connection timed out!'
@@ -2287,7 +2287,7 @@ def print_output(hosts,usejson = False,quiet = False,greppable = False):
2287
2287
 
2288
2288
  #%% ------------ Run / Process Hosts Block ----------------
2289
2289
  def processRunOnHosts(timeout, password, max_connections, hosts, returnUnfinished, no_watch, json, called, greppable,
2290
- unavailableHosts,willUpdateUnreachableHosts,curses_min_char_len = DEFAULT_CURSES_MINIMUM_CHAR_LEN,
2290
+ unavailableHosts:dict,willUpdateUnreachableHosts,curses_min_char_len = DEFAULT_CURSES_MINIMUM_CHAR_LEN,
2291
2291
  curses_min_line_len = DEFAULT_CURSES_MINIMUM_LINE_LEN,single_window = DEFAULT_SINGLE_WINDOW,
2292
2292
  unavailable_host_expiry = DEFAULT_UNAVAILABLE_HOST_EXPIRY):
2293
2293
  global __globalUnavailableHosts
@@ -2317,45 +2317,47 @@ def processRunOnHosts(timeout, password, max_connections, hosts, returnUnfinishe
2317
2317
  thread.join(timeout=3)
2318
2318
  # update the unavailable hosts and global unavailable hosts
2319
2319
  if willUpdateUnreachableHosts:
2320
- unavailableHosts = set(unavailableHosts)
2321
- unavailableHosts.update([host.name for host in hosts if host.stderr and ('No route to host' in host.stderr[0].strip() or (host.stderr[-1].strip().startswith('Timeout!') and host.returncode == 124))])
2322
- # reachable hosts = all hosts - unreachable hosts
2323
- reachableHosts = set([host.name for host in hosts]) - unavailableHosts
2320
+ availableHosts = set()
2321
+ for host in hosts:
2322
+ if host.stderr and ('No route to host' in host.stderr[0].strip() or 'Connection timed out' in host.stderr[0].strip() or (host.stderr[-1].strip().startswith('Timeout!') and host.returncode == 124)):
2323
+ unavailableHosts[host.name] = int(time.monotonic())
2324
+ __globalUnavailableHosts[host.name] = int(time.monotonic())
2325
+ else:
2326
+ availableHosts.add(host.name)
2327
+ if host.name in unavailableHosts:
2328
+ del unavailableHosts[host.name]
2329
+ if host.name in __globalUnavailableHosts:
2330
+ del __globalUnavailableHosts[host.name]
2324
2331
  if __DEBUG_MODE:
2325
2332
  print(f'Unreachable hosts: {unavailableHosts}')
2326
- __globalUnavailableHosts.update(unavailableHosts)
2327
-
2328
- # os.environ['__multiSSH3_UNAVAILABLE_HOSTS'] = ','.join(unavailableHosts)
2329
- # create a temporary file to store the unavailable hosts
2330
2333
  try:
2331
2334
  # check for the old content, only update if the new content is different
2332
- if not os.path.exists(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')):
2333
- with open(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv'),'w') as f:
2334
- f.write(f',{int(time.monotonic())}\n'.join(unavailableHosts) + f',{int(time.monotonic())}\n')
2335
+ if not os.path.exists(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv')):
2336
+ with open(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv'),'w') as f:
2337
+ f.writelines(f'{host},{expTime}' for host,expTime in unavailableHosts.values())
2335
2338
  else:
2336
2339
  oldDic = {}
2337
2340
  try:
2338
- with open(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv'),'r') as f:
2341
+ with open(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv'),'r') as f:
2339
2342
  for line in f:
2340
2343
  line = line.strip()
2341
2344
  if line and ',' in line and len(line.split(',')) >= 2 and line.split(',')[0] and line.split(',')[1].isdigit():
2342
- oldDic[line.split(',')[0]] = int(line.split(',')[1])
2345
+ hostname = line.split(',')[0]
2346
+ expireTime = int(line.split(',')[1])
2347
+ if expireTime < time.monotonic() and hostname not in availableHosts:
2348
+ oldDic[hostname] = expireTime
2343
2349
  except:
2344
2350
  pass
2345
- for key in list(oldDic.keys()):
2346
- if key in reachableHosts or time.monotonic() < oldDic[key] or time.monotonic() - oldDic[key] > unavailable_host_expiry:
2347
- del oldDic[key]
2348
2351
  # add new entries
2349
- for host in unavailableHosts:
2350
- if host not in oldDic:
2351
- oldDic[host] = int(time.monotonic ())
2352
- with open(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv.new'),'w') as f:
2352
+ oldDic.update(unavailableHosts)
2353
+ with open(os.path.join(tempfile.gettempdir(),getpass.getuser()+'__multiSSH3_UNAVAILABLE_HOSTS.csv.new'),'w') as f:
2353
2354
  for key, value in oldDic.items():
2354
2355
  f.write(f'{key},{value}\n')
2355
- os.replace(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv.new'),os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv'))
2356
-
2356
+ os.replace(os.path.join(tempfile.gettempdir(),getpass.getuser()+'__multiSSH3_UNAVAILABLE_HOSTS.csv.new'),os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv'))
2357
2357
  except Exception as e:
2358
2358
  eprint(f'Error writing to temporary file: {e!r}')
2359
+ import traceback
2360
+ eprint(traceback.format_exc())
2359
2361
 
2360
2362
  # print the output, if the output of multiple hosts are the same, we aggragate them
2361
2363
  if not called:
@@ -2566,27 +2568,29 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2566
2568
  record_command_history(locals())
2567
2569
  if error_only:
2568
2570
  __global_suppress_printout = True
2569
- if os.path.exists(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')):
2571
+ if os.path.exists(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv')):
2570
2572
  if unavailable_host_expiry <= 0:
2571
2573
  unavailable_host_expiry = 10
2572
2574
  try:
2573
2575
  readed = False
2574
- if 0 < time.time() - os.path.getmtime(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')) < unavailable_host_expiry:
2576
+ if 0 < time.time() - os.path.getmtime(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv')) < unavailable_host_expiry:
2575
2577
 
2576
- with open(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv'),'r') as f:
2578
+ with open(os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv'),'r') as f:
2577
2579
  for line in f:
2578
2580
  line = line.strip()
2579
2581
  if line and ',' in line and len(line.split(',')) >= 2 and line.split(',')[0] and line.split(',')[1].isdigit():
2580
- if int(line.split(',')[1]) < time.monotonic() and int(line.split(',')[1]) + unavailable_host_expiry > time.monotonic():
2581
- __globalUnavailableHosts.add(line.split(',')[0])
2582
+ hostname = line.split(',')[0]
2583
+ expireTime = int(line.split(',')[1])
2584
+ if expireTime < time.monotonic() and expireTime + unavailable_host_expiry > time.monotonic():
2585
+ __globalUnavailableHosts[hostname] = expireTime
2582
2586
  readed = True
2583
2587
  if readed and not __global_suppress_printout:
2584
- eprint(f"Read unavailable hosts from the file {os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')}")
2588
+ eprint(f"Read unavailable hosts from the file {os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv')}")
2585
2589
  except Exception as e:
2586
- eprint(f"Warning: Unable to read the unavailable hosts from the file {os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')!r}")
2590
+ eprint(f"Warning: Unable to read the unavailable hosts from the file {os.path.join(tempfile.gettempdir(),f'__{getpass.getuser()}_multiSSH3_UNAVAILABLE_HOSTS.csv')!r}")
2587
2591
  eprint(str(e))
2588
2592
  elif '__multiSSH3_UNAVAILABLE_HOSTS' in readEnvFromFile():
2589
- __globalUnavailableHosts.update(readEnvFromFile()['__multiSSH3_UNAVAILABLE_HOSTS'].split(','))
2593
+ __globalUnavailableHosts.update({host: int(time.monotonic()) for host in readEnvFromFile()['__multiSSH3_UNAVAILABLE_HOSTS'].split(',') if host})
2590
2594
  if not max_connections:
2591
2595
  max_connections = 4 * os.cpu_count()
2592
2596
  elif max_connections == 0:
@@ -2619,7 +2623,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2619
2623
  if skipUnreachable:
2620
2624
  unavailableHosts = __globalUnavailableHosts
2621
2625
  else:
2622
- unavailableHosts = set()
2626
+ unavailableHosts = dict()
2623
2627
  # set global input to empty
2624
2628
  __keyPressesIn = [[]]
2625
2629
  __global_suppress_printout = True
@@ -2628,7 +2632,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2628
2632
  if skipUnreachable:
2629
2633
  unavailableHosts = __globalUnavailableHosts
2630
2634
  else:
2631
- unavailableHosts = set()
2635
+ unavailableHosts = dict()
2632
2636
  skipUnreachable = True
2633
2637
  if quiet:
2634
2638
  __global_suppress_printout = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 5.68
3
+ Version: 5.69
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
@@ -0,0 +1,6 @@
1
+ multiSSH3.py,sha256=Fh69JiPpBe27zs05RmXa8z1CQmaNXh7hVIZl_RN2iGY,144513
2
+ multissh3-5.69.dist-info/METADATA,sha256=2PFFZiQ4Jck3s1L0DjzHtNerxXPGaxe3maZ8_UOonhU,18093
3
+ multissh3-5.69.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
4
+ multissh3-5.69.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
5
+ multissh3-5.69.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
6
+ multissh3-5.69.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,6 +0,0 @@
1
- multiSSH3.py,sha256=_Lr8Q00fGgVkJAtP6NXZGnfipNa5yqiZplxg5ZDXvqI,144176
2
- multissh3-5.68.dist-info/METADATA,sha256=mwLzCxpuDPk_k0JqGCGE0CMVOmg9DBs7GHNOPtNvJ6A,18093
3
- multissh3-5.68.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
4
- multissh3-5.68.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
5
- multissh3-5.68.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
6
- multissh3-5.68.dist-info/RECORD,,