multiSSH3 4.97__py3-none-any.whl → 4.98__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-4.97.dist-info → multiSSH3-4.98.dist-info}/METADATA +1 -1
- multiSSH3-4.98.dist-info/RECORD +7 -0
- multiSSH3.py +45 -6
- multiSSH3-4.97.dist-info/RECORD +0 -7
- {multiSSH3-4.97.dist-info → multiSSH3-4.98.dist-info}/LICENSE +0 -0
- {multiSSH3-4.97.dist-info → multiSSH3-4.98.dist-info}/WHEEL +0 -0
- {multiSSH3-4.97.dist-info → multiSSH3-4.98.dist-info}/entry_points.txt +0 -0
- {multiSSH3-4.97.dist-info → multiSSH3-4.98.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
multiSSH3.py,sha256=Dm7lML6_oR8yedBdlZCsR-J7Et2Xh5r149y1V666Avw,93039
|
|
2
|
+
multiSSH3-4.98.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
3
|
+
multiSSH3-4.98.dist-info/METADATA,sha256=hkcvqm3j6w0i24rVinCJMhoxax1btQbHoCaqG-SnJx0,16043
|
|
4
|
+
multiSSH3-4.98.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
5
|
+
multiSSH3-4.98.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
6
|
+
multiSSH3-4.98.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
7
|
+
multiSSH3-4.98.dist-info/RECORD,,
|
multiSSH3.py
CHANGED
|
@@ -30,7 +30,7 @@ except AttributeError:
|
|
|
30
30
|
# If neither is available, use a dummy decorator
|
|
31
31
|
def cache_decorator(func):
|
|
32
32
|
return func
|
|
33
|
-
version = '4.
|
|
33
|
+
version = '4.98'
|
|
34
34
|
VERSION = version
|
|
35
35
|
|
|
36
36
|
CONFIG_FILE = '/etc/multiSSH3.config.json'
|
|
@@ -115,6 +115,7 @@ __build_in_default_config = {
|
|
|
115
115
|
'_rsyncPath': None,
|
|
116
116
|
'_bashPath': None,
|
|
117
117
|
'__ERROR_MESSAGES_TO_IGNORE_REGEX':None,
|
|
118
|
+
'__DEBUG_MODE': False,
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
AUTHOR = __configs_from_file.get('AUTHOR', __build_in_default_config['AUTHOR'])
|
|
@@ -168,6 +169,8 @@ if __ERROR_MESSAGES_TO_IGNORE_REGEX:
|
|
|
168
169
|
else:
|
|
169
170
|
__ERROR_MESSAGES_TO_IGNORE_REGEX = re.compile('|'.join(ERROR_MESSAGES_TO_IGNORE))
|
|
170
171
|
|
|
172
|
+
__DEBUG_MODE = __configs_from_file.get('__DEBUG_MODE', __build_in_default_config['__DEBUG_MODE'])
|
|
173
|
+
|
|
171
174
|
|
|
172
175
|
|
|
173
176
|
__global_suppress_printout = True
|
|
@@ -191,8 +194,6 @@ def get_i():
|
|
|
191
194
|
|
|
192
195
|
class Host:
|
|
193
196
|
def __init__(self, name, command, files = None,ipmi = False,interface_ip_prefix = None,scp=False,extraargs=None,gatherMode=False):
|
|
194
|
-
global __host_i_counter
|
|
195
|
-
global __host_i_lock
|
|
196
197
|
self.name = name # the name of the host (hostname or IP address)
|
|
197
198
|
self.command = command # the command to run on the host
|
|
198
199
|
self.returncode = None # the return code of the command
|
|
@@ -675,6 +676,7 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
675
676
|
global __ERROR_MESSAGES_TO_IGNORE_REGEX
|
|
676
677
|
global __ipmiiInterfaceIPPrefix
|
|
677
678
|
global _binPaths
|
|
679
|
+
global __DEBUG_MODE
|
|
678
680
|
try:
|
|
679
681
|
keyCheckArgs = []
|
|
680
682
|
rsyncKeyCheckArgs = []
|
|
@@ -737,6 +739,8 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
737
739
|
formatedCMD = [_binPaths['ipmitool'],f'-H {host.address}',f'-U {host.username}'] + extraargs + [host.command]
|
|
738
740
|
elif 'ssh' in _binPaths:
|
|
739
741
|
host.output.append('Ipmitool not found on the local machine! Trying ipmitool on the remote machine...')
|
|
742
|
+
if __DEBUG_MODE:
|
|
743
|
+
host.stderr.append('Ipmitool not found on the local machine! Trying ipmitool on the remote machine...')
|
|
740
744
|
host.ipmi = False
|
|
741
745
|
host.interface_ip_prefix = None
|
|
742
746
|
host.command = 'ipmitool '+host.command if not host.command.startswith('ipmitool ') else host.command
|
|
@@ -754,6 +758,8 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
754
758
|
useScp = True
|
|
755
759
|
elif 'rsync' in _binPaths:
|
|
756
760
|
host.output.append('scp not found on the local machine! Trying to use rsync...')
|
|
761
|
+
if __DEBUG_MODE:
|
|
762
|
+
host.stderr.append('scp not found on the local machine! Trying to use rsync...')
|
|
757
763
|
useScp = False
|
|
758
764
|
else:
|
|
759
765
|
host.output.append('scp not found on the local machine! Please install scp or rsync to use file sync mode.')
|
|
@@ -764,6 +770,8 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
764
770
|
useScp = False
|
|
765
771
|
elif 'scp' in _binPaths:
|
|
766
772
|
host.output.append('rsync not found on the local machine! Trying to use scp...')
|
|
773
|
+
if __DEBUG_MODE:
|
|
774
|
+
host.stderr.append('rsync not found on the local machine! Trying to use scp...')
|
|
767
775
|
useScp = True
|
|
768
776
|
else:
|
|
769
777
|
host.output.append('rsync not found on the local machine! Please install rsync or scp to use file sync mode.')
|
|
@@ -784,7 +792,8 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
784
792
|
formatedCMD = [_binPaths['sshpass'], '-p', passwds] + formatedCMD
|
|
785
793
|
elif passwds:
|
|
786
794
|
host.output.append('Warning: sshpass is not available. Please install sshpass to use password authentication.')
|
|
787
|
-
|
|
795
|
+
if __DEBUG_MODE:
|
|
796
|
+
host.stderr.append('Warning: sshpass is not available. Please install sshpass to use password authentication.')
|
|
788
797
|
host.output.append('Please provide password via live input or use ssh key authentication.')
|
|
789
798
|
# # try to send the password via __keyPressesIn
|
|
790
799
|
# __keyPressesIn[-1] = list(passwds) + ['\n']
|
|
@@ -802,6 +811,8 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
802
811
|
with sem:
|
|
803
812
|
try:
|
|
804
813
|
host.output.append('Running command: '+' '.join(formatedCMD))
|
|
814
|
+
if __DEBUG_MODE:
|
|
815
|
+
host.stderr.append('Running command: '+' '.join(formatedCMD))
|
|
805
816
|
#host.stdout = []
|
|
806
817
|
proc = subprocess.Popen(formatedCMD,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE)
|
|
807
818
|
# create a thread to handle stdout
|
|
@@ -890,6 +901,8 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
890
901
|
if host.ipmi and host.returncode != 0 and any(['Unable to establish IPMI' in line for line in host.stderr]):
|
|
891
902
|
host.stderr = []
|
|
892
903
|
host.output.append('IPMI connection failed! Trying SSH connection...')
|
|
904
|
+
if __DEBUG_MODE:
|
|
905
|
+
host.stderr.append('IPMI connection failed! Trying SSH connection...')
|
|
893
906
|
host.ipmi = False
|
|
894
907
|
host.interface_ip_prefix = None
|
|
895
908
|
host.command = 'ipmitool '+host.command if not host.command.startswith('ipmitool ') else host.command
|
|
@@ -899,6 +912,8 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
899
912
|
host.stderr = []
|
|
900
913
|
host.stdout = []
|
|
901
914
|
host.output.append('Rsync connection failed! Trying SCP connection...')
|
|
915
|
+
if __DEBUG_MODE:
|
|
916
|
+
host.stderr.append('Rsync connection failed! Trying SCP connection...')
|
|
902
917
|
host.scp = True
|
|
903
918
|
ssh_command(host,sem,timeout,passwds)
|
|
904
919
|
|
|
@@ -1393,6 +1408,8 @@ def processRunOnHosts(timeout, password, max_connections, hosts, returnUnfinishe
|
|
|
1393
1408
|
# update the unavailable hosts and global unavailable hosts
|
|
1394
1409
|
if willUpdateUnreachableHosts:
|
|
1395
1410
|
unavailableHosts.update([host.name for host in hosts if host.stderr and ('No route to host' in host.stderr[0].strip() or host.stderr[0].strip().startswith('Timeout!'))])
|
|
1411
|
+
if __DEBUG_MODE:
|
|
1412
|
+
print(f'Unreachable hosts: {unavailableHosts}')
|
|
1396
1413
|
__globalUnavailableHosts.update(unavailableHosts)
|
|
1397
1414
|
# update the os environment variable if not _no_env
|
|
1398
1415
|
if not _no_env:
|
|
@@ -1523,6 +1540,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
1523
1540
|
global __global_suppress_printout
|
|
1524
1541
|
global _no_env
|
|
1525
1542
|
global _emo
|
|
1543
|
+
global __DEBUG_MODE
|
|
1526
1544
|
_emo = False
|
|
1527
1545
|
_no_env = no_env
|
|
1528
1546
|
if not no_env and '__multiSSH3_UNAVAILABLE_HOSTS' in os.environ:
|
|
@@ -1585,6 +1603,8 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
1585
1603
|
skipHostStr[i] = userStr + host
|
|
1586
1604
|
skipHostStr = ','.join(skipHostStr)
|
|
1587
1605
|
targetHostsList = expand_hostnames(frozenset(hostStr.split(',')))
|
|
1606
|
+
if __DEBUG_MODE:
|
|
1607
|
+
eprint(f"Target hosts: {targetHostsList}")
|
|
1588
1608
|
skipHostsList = expand_hostnames(frozenset(skipHostStr.split(',')))
|
|
1589
1609
|
if skipHostsList:
|
|
1590
1610
|
eprint(f"Skipping hosts: {skipHostsList}")
|
|
@@ -1614,6 +1634,8 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
1614
1634
|
files = []
|
|
1615
1635
|
else:
|
|
1616
1636
|
files = list(pathSet)
|
|
1637
|
+
if __DEBUG_MODE:
|
|
1638
|
+
eprint(f"Files: {files}")
|
|
1617
1639
|
if oneonone:
|
|
1618
1640
|
hosts = []
|
|
1619
1641
|
if len(commands) != len(targetHostsList) - len(skipHostsList):
|
|
@@ -1748,13 +1770,14 @@ def main():
|
|
|
1748
1770
|
global __ipmiiInterfaceIPPrefix
|
|
1749
1771
|
global _binPaths
|
|
1750
1772
|
global _env_file
|
|
1773
|
+
global __DEBUG_MODE
|
|
1751
1774
|
_emo = False
|
|
1752
1775
|
# We handle the signal
|
|
1753
1776
|
signal.signal(signal.SIGINT, signal_handler)
|
|
1754
1777
|
# We parse the arguments
|
|
1755
1778
|
parser = argparse.ArgumentParser(description=f'Run a command on multiple hosts, Use #HOST# or #HOSTNAME# to replace the host name in the command. Config file: {CONFIG_FILE}')
|
|
1756
1779
|
parser.add_argument('hosts', metavar='hosts', type=str, nargs='?', help=f'Hosts to run the command on, use "," to seperate hosts. (default: {DEFAULT_HOSTS})',default=DEFAULT_HOSTS)
|
|
1757
|
-
parser.add_argument('commands', metavar='commands', type=str, nargs='
|
|
1780
|
+
parser.add_argument('commands', metavar='commands', type=str, nargs='*',default=None,help='the command to run on the hosts / the destination of the files #HOST# or #HOSTNAME# will be replaced with the host name.')
|
|
1758
1781
|
parser.add_argument('-u','--username', type=str,help=f'The general username to use to connect to the hosts. Will get overwrote by individual username@host if specified. (default: {DEFAULT_USERNAME})',default=DEFAULT_USERNAME)
|
|
1759
1782
|
parser.add_argument('-p', '--password', type=str,help=f'The password to use to connect to the hosts, (default: {DEFAULT_PASSWORD})',default=DEFAULT_PASSWORD)
|
|
1760
1783
|
parser.add_argument('-ea','--extraargs',type=str,help=f'Extra arguments to pass to the ssh / rsync / scp command. Put in one string for multiple arguments.Use "=" ! Ex. -ea="--delete" (default: {DEFAULT_EXTRA_ARGS})',default=DEFAULT_EXTRA_ARGS)
|
|
@@ -1785,11 +1808,26 @@ def main():
|
|
|
1785
1808
|
parser.add_argument("-su","--skip_unreachable", action='store_true', help=f"Skip unreachable hosts while using --repeat. Note: Timedout Hosts are considered unreachable. Note: multiple command sequence will still auto skip unreachable hosts. (default: {DEFAULT_SKIP_UNREACHABLE})", default=DEFAULT_SKIP_UNREACHABLE)
|
|
1786
1809
|
parser.add_argument("-sh","--skip_hosts", type=str, help=f"Skip the hosts in the list. (default: {DEFAULT_SKIP_HOSTS if DEFAULT_SKIP_HOSTS else 'None'})", default=DEFAULT_SKIP_HOSTS)
|
|
1787
1810
|
parser.add_argument('--store_config_file', action='store_true', help=f'Store / generate the default config file from command line argument and current config at {CONFIG_FILE}')
|
|
1811
|
+
parser.add_argument('--debug', action='store_true', help='Print debug information')
|
|
1812
|
+
parser.add_argument('--copy-id', action='store_true', help='Copy the ssh id to the hosts')
|
|
1788
1813
|
parser.add_argument("-V","--version", action='version', version=f'%(prog)s {version} with [ {", ".join(_binPaths.keys())} ] by {AUTHOR} ({AUTHOR_EMAIL})')
|
|
1789
1814
|
|
|
1790
1815
|
# parser.add_argument('-u', '--user', metavar='user', type=str, nargs=1,
|
|
1791
1816
|
# help='the user to use to connect to the hosts')
|
|
1792
|
-
args = parser.parse_args()
|
|
1817
|
+
#args = parser.parse_args()
|
|
1818
|
+
|
|
1819
|
+
# if python version is 3.7 or higher, use parse_intermixed_args
|
|
1820
|
+
if sys.version_info >= (3,7):
|
|
1821
|
+
args = parser.parse_intermixed_args()
|
|
1822
|
+
else:
|
|
1823
|
+
# try to parse the arguments using parse_known_args
|
|
1824
|
+
args, unknown = parser.parse_known_args()
|
|
1825
|
+
# if there are unknown arguments, we will try to parse them again using parse_args
|
|
1826
|
+
if unknown:
|
|
1827
|
+
eprint(f"Warning: Unknown arguments, treating all as commands: {unknown}")
|
|
1828
|
+
args.commands += unknown
|
|
1829
|
+
|
|
1830
|
+
|
|
1793
1831
|
|
|
1794
1832
|
if args.store_config_file:
|
|
1795
1833
|
try:
|
|
@@ -1816,6 +1854,7 @@ def main():
|
|
|
1816
1854
|
sys.exit(0)
|
|
1817
1855
|
|
|
1818
1856
|
_env_file = args.env_file
|
|
1857
|
+
__DEBUG_MODE = args.debug
|
|
1819
1858
|
# if there are more than 1 commands, and every command only consists of one word,
|
|
1820
1859
|
# we will ask the user to confirm if they want to run multiple commands or just one command.
|
|
1821
1860
|
if not args.file and len(args.commands) > 1 and all([len(command.split()) == 1 for command in args.commands]):
|
multiSSH3-4.97.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
multiSSH3.py,sha256=WCp2vui9NMeviCPe39CVu9WY0-lOn3YEIzFGO8i8m7o,91417
|
|
2
|
-
multiSSH3-4.97.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
3
|
-
multiSSH3-4.97.dist-info/METADATA,sha256=ndgCxl2LHcQ3x88vqxJhTOd6ah7U46-Xva-Uj74oBzU,16043
|
|
4
|
-
multiSSH3-4.97.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
5
|
-
multiSSH3-4.97.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
6
|
-
multiSSH3-4.97.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
7
|
-
multiSSH3-4.97.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|