multiSSH3 6.34__tar.gz → 6.36__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.
- {multissh3-6.34/multiSSH3.egg-info → multissh3-6.36}/PKG-INFO +8 -6
- {multissh3-6.34 → multissh3-6.36}/README.md +7 -5
- {multissh3-6.34 → multissh3-6.36}/mssh_complete.py +61 -7
- {multissh3-6.34 → multissh3-6.36/multiSSH3.egg-info}/PKG-INFO +8 -6
- {multissh3-6.34 → multissh3-6.36}/multiSSH3.egg-info/SOURCES.txt +3 -0
- {multissh3-6.34 → multissh3-6.36}/multiSSH3.py +444 -120
- {multissh3-6.34 → multissh3-6.36}/tests/conftest.py +19 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_cli_option_words.py +102 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_context.py +70 -3
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_diagnose.py +20 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_picker.py +4 -2
- {multissh3-6.34 → multissh3-6.36}/tests/test_curses_event_loop.py +228 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_harness_smoke.py +13 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_options_after_command.py +14 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_runtime_branch_matrix.py +1 -1
- {multissh3-6.34 → multissh3-6.36}/tests/test_runtime_dir.py +28 -0
- multissh3-6.36/tests/test_shared_lines.py +219 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_stream_reader.py +7 -5
- multissh3-6.36/tests/test_streamed_output.py +228 -0
- multissh3-6.36/tests/test_waiting.py +244 -0
- {multissh3-6.34 → multissh3-6.36}/LICENSE +0 -0
- {multissh3-6.34 → multissh3-6.36}/MANIFEST.in +0 -0
- {multissh3-6.34 → multissh3-6.36}/multiSSH3.egg-info/dependency_links.txt +0 -0
- {multissh3-6.34 → multissh3-6.36}/multiSSH3.egg-info/entry_points.txt +0 -0
- {multissh3-6.34 → multissh3-6.36}/multiSSH3.egg-info/top_level.txt +0 -0
- {multissh3-6.34 → multissh3-6.36}/pytest.ini +0 -0
- {multissh3-6.34 → multissh3-6.36}/setup.cfg +0 -0
- {multissh3-6.34 → multissh3-6.36}/setup.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/bench_complete.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/complete_shell_harness.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/data/run_command_golden.json +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/shells/Dockerfile.bash5.1 +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/shells/Dockerfile.bash5.3 +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/shells/Dockerfile.centos7 +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/shells/Dockerfile.current +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/shells/Dockerfile.legacy +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/shells/Dockerfile.modern +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/shells/run_matrix.sh +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_cli_branches.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_cli_processing.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_closed_output.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_color_helpers.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_command_terminal.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_compact_hostnames.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_blesh.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_cli.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_editor.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_engine.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_eval.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_hosts.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_hoststate.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_index.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_keys.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_lex.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_mssh_helpers.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_password_spellings.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_probe.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_shells.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_start.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_store.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_suggest.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_terminal.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_untrusted.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_complete_zsh_options.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_config_cli.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_config_validate.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_config_values.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_control_master_config.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_copy_id.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_curses_branches.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_curses_tui.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_curses_wide_chars.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_expand_hostnames.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_file_trust.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_helpers_extra.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_history_replay.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_host.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_host_validity.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_hosts_file.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_identities_only.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_ipmi_lockout_wait.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_ipmi_logins.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_key_path_home.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_login_drop_retry.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_main_lifecycle.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_narrow_terminal.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_no_hosts.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_option_no_effect.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_output_merge.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_output_robustness.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_packaging.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_password_handling.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_passwords_out_of_ps.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_run_command.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_run_command_branches.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_run_command_characterization.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_run_command_exit.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_run_on_hosts.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_run_on_hosts_modes.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_smoke_optional.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_ssh_error_text.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_ssh_timeouts.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_sshpass_exit.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_start_run_on_hosts.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_stopped_commands.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_tui_ssh_notices.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_unavailable_hosts.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_underscore_settings.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/test_wide_wrap.py +0 -0
- {multissh3-6.34 → multissh3-6.36}/tests/vt_grid.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multiSSH3
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.36
|
|
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
|
|
@@ -603,10 +603,12 @@ mssh -q -- web1 grep -r TODO /srv
|
|
|
603
603
|
|
|
604
604
|
### `-ea, --extraargs EXTRAARGS` | _`DEFAULT_EXTRA_ARGS`_
|
|
605
605
|
- Extra arguments to pass to the ssh / rsync / scp command. Put in one string for multiple arguments.
|
|
606
|
+
- The word after `-ea` is its value, also when it starts with `-`: `-ea '-p 2222'`, `-ea -A`. Quote an option together with its value: with `-ea -p 2222` the quotes were forgotten, and mssh stops, as it does for a password option there. ( mssh 6.34 and older need `-ea='-p 2222'`, which still works. )
|
|
606
607
|
- The string is split into arguments as a shell would: quote an argument that contains spaces.
|
|
607
608
|
- For a file copy with rsync, ssh's options that take a value go to the ssh that rsync runs, and the other arguments to rsync: the option followed by its value as the next word (`-o ConnectTimeout=5`, `-p 2222`, `-i FILE`, `-J HOST`, `-F FILE`, `-l USER`, ...), or `-oName=value` and `-pPORT`. scp takes ssh's options itself; ssh's `-p PORT` is given to it as `-P PORT`.
|
|
608
609
|
- Example:
|
|
609
610
|
```bash
|
|
611
|
+
mssh -ea '-p 2222' allServers uptime
|
|
610
612
|
mssh -ea="--delete" -f ./data/ allServers /tmp/data/
|
|
611
613
|
mssh -ea="-o ProxyCommand='ssh -W %h:%p bastion'" allServers uptime
|
|
612
614
|
```
|
|
@@ -717,7 +719,7 @@ mssh -f ./data/ allServers /tmp/data/
|
|
|
717
719
|
|
|
718
720
|
### `-ia, --ipmi_args IPMI_ARGS` | _`DEFAULT_IPMI_ARGS`_
|
|
719
721
|
- Extra arguments for ipmitool (or redfishtool) in `--ipmi` mode, in one string, placed as they are before the command. `-ea` arguments only go to ssh / scp / rsync.
|
|
720
|
-
- Ex: `mssh -M -ia
|
|
722
|
+
- Ex: `mssh -M -ia '-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. The word after `-ia` is its value, also when it starts with `-`; quote an option together with its value ( mssh 6.34 and older need `-ia='-I lanplus'` ).
|
|
721
723
|
|
|
722
724
|
### `-im, --ipmi_method IPMI_METHOD` | _`DEFAULT_IPMI_METHOD`_
|
|
723
725
|
- How `--ipmi` reaches a host:
|
|
@@ -824,7 +826,7 @@ mssh -f ./data/ allServers /tmp/data/
|
|
|
824
826
|
- By default mssh set this to true to speed up operations on large host lists with some unreachable hosts.
|
|
825
827
|
- Unreachable hosts will be tried again when their timeout expires.
|
|
826
828
|
- A skipped host is not run and counts as a failed host: it shows return code 255 ( what ssh returns when it cannot connect ) with a line saying it was skipped, and the summary lists it after `failed_hosts:` as `Skipped N hosts unreachable in a recent run, without trying them: ...`. So mssh does not exit 0 or print `Success` when it skipped a host.
|
|
827
|
-
- mssh stores the current run unreachable hosts in memory and if `skip_unreachable` is true, it will store them in `unavailable_hosts.csv` in mssh's private runtime directory: `$XDG_RUNTIME_DIR/multiSSH3`, else `/run/user/<uid>/multiSSH3`, `/dev/shm/multiSSH3-<uid>`, or a `multiSSH3-<uid>` folder in the system temp folder (the first that only you can write to). If none can be used, they are only kept for the current run.
|
|
829
|
+
- mssh stores the current run unreachable hosts in memory and if `skip_unreachable` is true, it will store them in `unavailable_hosts.csv` in mssh's private runtime directory: `$XDG_RUNTIME_DIR/multiSSH3`, else `/run/user/<uid>/multiSSH3`, `/dev/shm/multiSSH3-<uid>`, or a `multiSSH3-<uid>` folder in the system temp folder (the first that only you can write to, or could make). mssh reads them back from that one only, not from a later place an older session left them in. If none can be used, they are only kept for the current run.
|
|
828
830
|
- To force mssh to not use unreachable hosts from previous runs, you can use `-a, -nsu, --no_skip_unreachable` to set `skip_unreachable` to false.
|
|
829
831
|
|
|
830
832
|
### `-a, -nsu, --no_skip_unreachable` | not _`DEFAULT_SKIP_UNREACHABLE`_
|
|
@@ -972,7 +974,7 @@ Option names count in full only: `--user` is not `--username` and `-c` is not `-
|
|
|
972
974
|
|
|
973
975
|
Putting the command after `--` is no substitute for quotes: every word after the hosts is a command of its own, so `mssh web1 -- bash -c 'echo hi'` runs `bash`, `-c` and `echo hi` one after another (when each word is a single word, as in `mssh web1 -- ls -la`, mssh asks whether to join them).
|
|
974
976
|
|
|
975
|
-
Below is a sample help message output from multiSSH3 6.
|
|
977
|
+
Below is a sample help message output from multiSSH3 6.36 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
|
|
976
978
|
|
|
977
979
|
```bash
|
|
978
980
|
$ mssh -h
|
|
@@ -1003,7 +1005,7 @@ options:
|
|
|
1003
1005
|
The identity file to use to connect to the hosts. Implies --use_key. Specify a folder for program to search for a key. Use option without value to use ~/.ssh/ (default: None)
|
|
1004
1006
|
-uk, --use_key Attempt to use public key file to connect to the hosts. (default: False)
|
|
1005
1007
|
-ea, --extraargs EXTRAARGS
|
|
1006
|
-
Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments.
|
|
1008
|
+
Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments. The word after -ea is its value, also when it starts with "-": quote an option with its value. Ex. -ea "-p 2222 -A" (default: None)
|
|
1007
1009
|
-11, --oneonone Run one corresponding command on each host. (default: False)
|
|
1008
1010
|
-f, --file FILE The file to be copied to the hosts. Use -f multiple times to copy multiple files
|
|
1009
1011
|
-s, -fs, --file_sync [FILE_SYNC]
|
|
@@ -1028,7 +1030,7 @@ options:
|
|
|
1028
1030
|
-ip, --ipmi_password IPMI_PASSWORD
|
|
1029
1031
|
The password to use to connect to the hosts via ipmi. (default: set, not shown)
|
|
1030
1032
|
-ia, --ipmi_args IPMI_ARGS
|
|
1031
|
-
The arguments passed to ipmitool to use to connect to the hosts via ipmi. (default: )
|
|
1033
|
+
The arguments passed to ipmitool to use to connect to the hosts via ipmi. The word after -ia is its value, also when it starts with "-": quote an option with its value. Ex. -ia "-I lanplus" (default: )
|
|
1032
1034
|
-im, --ipmi_method IPMI_METHOD
|
|
1033
1035
|
The method to use to connect to the hosts via ipmi. (default: ipmitool)
|
|
1034
1036
|
-irlt, --ipmi_retry_lockout_time IPMI_RETRY_LOCKOUT_TIME
|
|
@@ -577,10 +577,12 @@ mssh -q -- web1 grep -r TODO /srv
|
|
|
577
577
|
|
|
578
578
|
### `-ea, --extraargs EXTRAARGS` | _`DEFAULT_EXTRA_ARGS`_
|
|
579
579
|
- Extra arguments to pass to the ssh / rsync / scp command. Put in one string for multiple arguments.
|
|
580
|
+
- The word after `-ea` is its value, also when it starts with `-`: `-ea '-p 2222'`, `-ea -A`. Quote an option together with its value: with `-ea -p 2222` the quotes were forgotten, and mssh stops, as it does for a password option there. ( mssh 6.34 and older need `-ea='-p 2222'`, which still works. )
|
|
580
581
|
- The string is split into arguments as a shell would: quote an argument that contains spaces.
|
|
581
582
|
- For a file copy with rsync, ssh's options that take a value go to the ssh that rsync runs, and the other arguments to rsync: the option followed by its value as the next word (`-o ConnectTimeout=5`, `-p 2222`, `-i FILE`, `-J HOST`, `-F FILE`, `-l USER`, ...), or `-oName=value` and `-pPORT`. scp takes ssh's options itself; ssh's `-p PORT` is given to it as `-P PORT`.
|
|
582
583
|
- Example:
|
|
583
584
|
```bash
|
|
585
|
+
mssh -ea '-p 2222' allServers uptime
|
|
584
586
|
mssh -ea="--delete" -f ./data/ allServers /tmp/data/
|
|
585
587
|
mssh -ea="-o ProxyCommand='ssh -W %h:%p bastion'" allServers uptime
|
|
586
588
|
```
|
|
@@ -691,7 +693,7 @@ mssh -f ./data/ allServers /tmp/data/
|
|
|
691
693
|
|
|
692
694
|
### `-ia, --ipmi_args IPMI_ARGS` | _`DEFAULT_IPMI_ARGS`_
|
|
693
695
|
- Extra arguments for ipmitool (or redfishtool) in `--ipmi` mode, in one string, placed as they are before the command. `-ea` arguments only go to ssh / scp / rsync.
|
|
694
|
-
- Ex: `mssh -M -ia
|
|
696
|
+
- Ex: `mssh -M -ia '-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. The word after `-ia` is its value, also when it starts with `-`; quote an option together with its value ( mssh 6.34 and older need `-ia='-I lanplus'` ).
|
|
695
697
|
|
|
696
698
|
### `-im, --ipmi_method IPMI_METHOD` | _`DEFAULT_IPMI_METHOD`_
|
|
697
699
|
- How `--ipmi` reaches a host:
|
|
@@ -798,7 +800,7 @@ mssh -f ./data/ allServers /tmp/data/
|
|
|
798
800
|
- By default mssh set this to true to speed up operations on large host lists with some unreachable hosts.
|
|
799
801
|
- Unreachable hosts will be tried again when their timeout expires.
|
|
800
802
|
- A skipped host is not run and counts as a failed host: it shows return code 255 ( what ssh returns when it cannot connect ) with a line saying it was skipped, and the summary lists it after `failed_hosts:` as `Skipped N hosts unreachable in a recent run, without trying them: ...`. So mssh does not exit 0 or print `Success` when it skipped a host.
|
|
801
|
-
- mssh stores the current run unreachable hosts in memory and if `skip_unreachable` is true, it will store them in `unavailable_hosts.csv` in mssh's private runtime directory: `$XDG_RUNTIME_DIR/multiSSH3`, else `/run/user/<uid>/multiSSH3`, `/dev/shm/multiSSH3-<uid>`, or a `multiSSH3-<uid>` folder in the system temp folder (the first that only you can write to). If none can be used, they are only kept for the current run.
|
|
803
|
+
- mssh stores the current run unreachable hosts in memory and if `skip_unreachable` is true, it will store them in `unavailable_hosts.csv` in mssh's private runtime directory: `$XDG_RUNTIME_DIR/multiSSH3`, else `/run/user/<uid>/multiSSH3`, `/dev/shm/multiSSH3-<uid>`, or a `multiSSH3-<uid>` folder in the system temp folder (the first that only you can write to, or could make). mssh reads them back from that one only, not from a later place an older session left them in. If none can be used, they are only kept for the current run.
|
|
802
804
|
- To force mssh to not use unreachable hosts from previous runs, you can use `-a, -nsu, --no_skip_unreachable` to set `skip_unreachable` to false.
|
|
803
805
|
|
|
804
806
|
### `-a, -nsu, --no_skip_unreachable` | not _`DEFAULT_SKIP_UNREACHABLE`_
|
|
@@ -946,7 +948,7 @@ Option names count in full only: `--user` is not `--username` and `-c` is not `-
|
|
|
946
948
|
|
|
947
949
|
Putting the command after `--` is no substitute for quotes: every word after the hosts is a command of its own, so `mssh web1 -- bash -c 'echo hi'` runs `bash`, `-c` and `echo hi` one after another (when each word is a single word, as in `mssh web1 -- ls -la`, mssh asks whether to join them).
|
|
948
950
|
|
|
949
|
-
Below is a sample help message output from multiSSH3 6.
|
|
951
|
+
Below is a sample help message output from multiSSH3 6.36 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
|
|
950
952
|
|
|
951
953
|
```bash
|
|
952
954
|
$ mssh -h
|
|
@@ -977,7 +979,7 @@ options:
|
|
|
977
979
|
The identity file to use to connect to the hosts. Implies --use_key. Specify a folder for program to search for a key. Use option without value to use ~/.ssh/ (default: None)
|
|
978
980
|
-uk, --use_key Attempt to use public key file to connect to the hosts. (default: False)
|
|
979
981
|
-ea, --extraargs EXTRAARGS
|
|
980
|
-
Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments.
|
|
982
|
+
Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments. The word after -ea is its value, also when it starts with "-": quote an option with its value. Ex. -ea "-p 2222 -A" (default: None)
|
|
981
983
|
-11, --oneonone Run one corresponding command on each host. (default: False)
|
|
982
984
|
-f, --file FILE The file to be copied to the hosts. Use -f multiple times to copy multiple files
|
|
983
985
|
-s, -fs, --file_sync [FILE_SYNC]
|
|
@@ -1002,7 +1004,7 @@ options:
|
|
|
1002
1004
|
-ip, --ipmi_password IPMI_PASSWORD
|
|
1003
1005
|
The password to use to connect to the hosts via ipmi. (default: set, not shown)
|
|
1004
1006
|
-ia, --ipmi_args IPMI_ARGS
|
|
1005
|
-
The arguments passed to ipmitool to use to connect to the hosts via ipmi. (default: )
|
|
1007
|
+
The arguments passed to ipmitool to use to connect to the hosts via ipmi. The word after -ia is its value, also when it starts with "-": quote an option with its value. Ex. -ia "-I lanplus" (default: )
|
|
1006
1008
|
-im, --ipmi_method IPMI_METHOD
|
|
1007
1009
|
The method to use to connect to the hosts via ipmi. (default: ipmitool)
|
|
1008
1010
|
-irlt, --ipmi_retry_lockout_time IPMI_RETRY_LOCKOUT_TIME
|
|
@@ -16,7 +16,7 @@ import sys
|
|
|
16
16
|
import time
|
|
17
17
|
from bisect import bisect_left
|
|
18
18
|
|
|
19
|
-
version = '0.
|
|
19
|
+
version = '0.9'
|
|
20
20
|
__version__ = version
|
|
21
21
|
PROTOCOL = 'MSSHC1'
|
|
22
22
|
MSSH_NAMES = ('mssh', 'mssh3', 'multissh', 'multissh3', 'multiSSH3')
|
|
@@ -166,6 +166,31 @@ def split_at_cursor(before, after):
|
|
|
166
166
|
|
|
167
167
|
#%% ------------ Option spec ----------------
|
|
168
168
|
PASSWORD_DESTS = frozenset(('password', 'ipmi_password'))
|
|
169
|
+
# options passing options on to another program (-ea, -ia): mssh 6.35+ takes the word after them as
|
|
170
|
+
# their value even when it starts with '-' (its _with_passed_on_values), except as takes_passed_on_value says
|
|
171
|
+
PASSED_ON_DESTS = frozenset(('extraargs', 'ipmi_args'))
|
|
172
|
+
# the letters of the options needing a value of their own, by where they pass them: ssh, ipmitool
|
|
173
|
+
PASSED_ON_BARE_VALUE_LETTERS = {
|
|
174
|
+
'extraargs': frozenset('BbcDEeFIiJLlmOoPpQRSWw'),
|
|
175
|
+
'ipmi_args': frozenset('AbBCdefHIklLmNoOpPRtTUyz'),
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
def takes_passed_on_value(dest, text, spec):
|
|
179
|
+
'''
|
|
180
|
+
Whether -ea or -ia (dest) takes text, the word after it, as its value, as mssh's _takes_passed_on_value
|
|
181
|
+
reads it: not a bare option needing a value of its own (-ea -p 2222: the quotes were forgotten), nor a
|
|
182
|
+
word giving mssh's password; mssh stops at those.
|
|
183
|
+
'''
|
|
184
|
+
if not text.startswith('-') or ' ' in text:
|
|
185
|
+
return True
|
|
186
|
+
if len(text) == 2 and text[1] in PASSED_ON_BARE_VALUE_LETTERS[dest]:
|
|
187
|
+
return False
|
|
188
|
+
if text.startswith('-ip'):
|
|
189
|
+
return False
|
|
190
|
+
if text[:2] == '-p' and text[2:].isdigit():
|
|
191
|
+
return True
|
|
192
|
+
option = spec.lookup(text)[0]
|
|
193
|
+
return option is None or option.dest not in PASSWORD_DESTS
|
|
169
194
|
# options naming files mssh reads its hosts and env aliases from, at Enter
|
|
170
195
|
LOOKUP_DESTS = frozenset(('env_file', 'env_files', 'host_file', 'config_file'))
|
|
171
196
|
PATH_DESTS = frozenset(('file', 'file_sync', 'env_file', 'env_files', 'config_file', 'store_config_file', 'host_file', 'history_file'))
|
|
@@ -506,9 +531,12 @@ def assign_roles(words, spec, current=None):
|
|
|
506
531
|
pending = None
|
|
507
532
|
# the word after a password option is its password, whatever it looks like: argparse stops
|
|
508
533
|
# at "-p -secret", but the shell's history keeps the line, and a password is never another word.
|
|
509
|
-
#
|
|
510
|
-
# (-
|
|
511
|
-
|
|
534
|
+
# mssh takes the word after -ea and -ia as their value (PASSED_ON_DESTS). argparse takes a dash
|
|
535
|
+
# word as the value when it holds a space and names no option (-u "-o x"); mssh's -11 makes it
|
|
536
|
+
# read negative numbers as options
|
|
537
|
+
# (a word being typed in an open quote will hold more: "-I lanplus")
|
|
538
|
+
passed_on = option.dest in PASSED_ON_DESTS and (takes_passed_on_value(option.dest, text, spec) or (is_current and token.open_quote))
|
|
539
|
+
if not dashed or option.dest in PASSWORD_DESTS or passed_on or (' ' in text and not spec.names_option(text)):
|
|
512
540
|
roles.append((ROLE_VALUE, option))
|
|
513
541
|
continue
|
|
514
542
|
if text == '--' and not is_current and not after_separator:
|
|
@@ -1422,6 +1450,21 @@ def _private_dir(path, create=True):
|
|
|
1422
1450
|
return False
|
|
1423
1451
|
return stat.S_ISDIR(st.st_mode) and st.st_uid == os.getuid() and not st.st_mode & 0o077
|
|
1424
1452
|
|
|
1453
|
+
def _could_be_private_dir(path):
|
|
1454
|
+
'''Whether _private_dir(path, create=True) would take path, without changing anything (as multiSSH3's).'''
|
|
1455
|
+
import stat
|
|
1456
|
+
try:
|
|
1457
|
+
st = os.lstat(path)
|
|
1458
|
+
except FileNotFoundError:
|
|
1459
|
+
return os.access(os.path.dirname(path), os.W_OK | os.X_OK)
|
|
1460
|
+
except OSError:
|
|
1461
|
+
return False
|
|
1462
|
+
if not stat.S_ISDIR(st.st_mode):
|
|
1463
|
+
return False
|
|
1464
|
+
if not hasattr(os, 'getuid'):
|
|
1465
|
+
return True
|
|
1466
|
+
return st.st_uid == os.getuid() and not st.st_mode & 0o022
|
|
1467
|
+
|
|
1425
1468
|
def _owned_and_not_writable_by_others(path):
|
|
1426
1469
|
if not hasattr(os, 'getuid'):
|
|
1427
1470
|
return True
|
|
@@ -1440,7 +1483,8 @@ def cache_dir(create=True):
|
|
|
1440
1483
|
mssh's private runtime directory, where unavailable_hosts.csv and the index live, or None.
|
|
1441
1484
|
|
|
1442
1485
|
Exactly as multiSSH3.runtime_dir picks it: $XDG_RUNTIME_DIR/multiSSH3 when that runtime dir
|
|
1443
|
-
is ours, /run/user/<uid>/multiSSH3, /dev/shm/multiSSH3-<uid>, then <tempdir>/multiSSH3-<uid
|
|
1486
|
+
is ours, /run/user/<uid>/multiSSH3, /dev/shm/multiSSH3-<uid>, then <tempdir>/multiSSH3-<uid>;
|
|
1487
|
+
without create, the one a write would use if it is there already, else None.
|
|
1444
1488
|
'''
|
|
1445
1489
|
import tempfile
|
|
1446
1490
|
if hasattr(os, 'getuid'):
|
|
@@ -1460,8 +1504,11 @@ def cache_dir(create=True):
|
|
|
1460
1504
|
candidates.append(os.path.join(_SHM_DIR, 'multiSSH3-{}'.format(user_id)))
|
|
1461
1505
|
candidates.append(os.path.join(tempfile.gettempdir(), 'multiSSH3-{}'.format(user_id)))
|
|
1462
1506
|
for directory in candidates:
|
|
1463
|
-
if
|
|
1464
|
-
|
|
1507
|
+
if create:
|
|
1508
|
+
if _private_dir(directory):
|
|
1509
|
+
return directory
|
|
1510
|
+
elif _could_be_private_dir(directory):
|
|
1511
|
+
return directory if _private_dir(directory, create=False) else None
|
|
1465
1512
|
return None
|
|
1466
1513
|
|
|
1467
1514
|
def _debug(message):
|
|
@@ -3616,10 +3663,16 @@ class Engine(object):
|
|
|
3616
3663
|
have_hosts = False
|
|
3617
3664
|
run = None # where the unquoted remote command starts in argv
|
|
3618
3665
|
pending = None # nargs of an option still taking the words that follow as values
|
|
3666
|
+
passed_on = None # -ea's or -ia's dest: the word may be its value even when it starts with '-'
|
|
3619
3667
|
paths = False
|
|
3620
3668
|
read = []
|
|
3621
3669
|
stop = len(argv)
|
|
3622
3670
|
for index, token in enumerate(argv):
|
|
3671
|
+
if passed_on:
|
|
3672
|
+
dest, passed_on = passed_on, None
|
|
3673
|
+
if takes_passed_on_value(dest, token.text, self.spec):
|
|
3674
|
+
pending = None
|
|
3675
|
+
continue
|
|
3623
3676
|
if token.text == '--':
|
|
3624
3677
|
stop = index
|
|
3625
3678
|
break
|
|
@@ -3642,6 +3695,7 @@ class Engine(object):
|
|
|
3642
3695
|
option, attached, _ = readings[-1]
|
|
3643
3696
|
if attached is None and option.nargs != '0':
|
|
3644
3697
|
pending = option.nargs
|
|
3698
|
+
passed_on = option.dest if option.dest in PASSED_ON_DESTS else None
|
|
3645
3699
|
if run is None and any(option.dest in ('file', 'file_sync', 'gather_mode') for option, _, _ in readings):
|
|
3646
3700
|
paths = True
|
|
3647
3701
|
if run is not None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multiSSH3
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.36
|
|
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
|
|
@@ -603,10 +603,12 @@ mssh -q -- web1 grep -r TODO /srv
|
|
|
603
603
|
|
|
604
604
|
### `-ea, --extraargs EXTRAARGS` | _`DEFAULT_EXTRA_ARGS`_
|
|
605
605
|
- Extra arguments to pass to the ssh / rsync / scp command. Put in one string for multiple arguments.
|
|
606
|
+
- The word after `-ea` is its value, also when it starts with `-`: `-ea '-p 2222'`, `-ea -A`. Quote an option together with its value: with `-ea -p 2222` the quotes were forgotten, and mssh stops, as it does for a password option there. ( mssh 6.34 and older need `-ea='-p 2222'`, which still works. )
|
|
606
607
|
- The string is split into arguments as a shell would: quote an argument that contains spaces.
|
|
607
608
|
- For a file copy with rsync, ssh's options that take a value go to the ssh that rsync runs, and the other arguments to rsync: the option followed by its value as the next word (`-o ConnectTimeout=5`, `-p 2222`, `-i FILE`, `-J HOST`, `-F FILE`, `-l USER`, ...), or `-oName=value` and `-pPORT`. scp takes ssh's options itself; ssh's `-p PORT` is given to it as `-P PORT`.
|
|
608
609
|
- Example:
|
|
609
610
|
```bash
|
|
611
|
+
mssh -ea '-p 2222' allServers uptime
|
|
610
612
|
mssh -ea="--delete" -f ./data/ allServers /tmp/data/
|
|
611
613
|
mssh -ea="-o ProxyCommand='ssh -W %h:%p bastion'" allServers uptime
|
|
612
614
|
```
|
|
@@ -717,7 +719,7 @@ mssh -f ./data/ allServers /tmp/data/
|
|
|
717
719
|
|
|
718
720
|
### `-ia, --ipmi_args IPMI_ARGS` | _`DEFAULT_IPMI_ARGS`_
|
|
719
721
|
- Extra arguments for ipmitool (or redfishtool) in `--ipmi` mode, in one string, placed as they are before the command. `-ea` arguments only go to ssh / scp / rsync.
|
|
720
|
-
- Ex: `mssh -M -ia
|
|
722
|
+
- Ex: `mssh -M -ia '-I lanplus' bmc[1-4] 'power status'` runs `ipmitool -H <address> -U <user> -E -I lanplus power status`, with the password in its environment. The word after `-ia` is its value, also when it starts with `-`; quote an option together with its value ( mssh 6.34 and older need `-ia='-I lanplus'` ).
|
|
721
723
|
|
|
722
724
|
### `-im, --ipmi_method IPMI_METHOD` | _`DEFAULT_IPMI_METHOD`_
|
|
723
725
|
- How `--ipmi` reaches a host:
|
|
@@ -824,7 +826,7 @@ mssh -f ./data/ allServers /tmp/data/
|
|
|
824
826
|
- By default mssh set this to true to speed up operations on large host lists with some unreachable hosts.
|
|
825
827
|
- Unreachable hosts will be tried again when their timeout expires.
|
|
826
828
|
- A skipped host is not run and counts as a failed host: it shows return code 255 ( what ssh returns when it cannot connect ) with a line saying it was skipped, and the summary lists it after `failed_hosts:` as `Skipped N hosts unreachable in a recent run, without trying them: ...`. So mssh does not exit 0 or print `Success` when it skipped a host.
|
|
827
|
-
- mssh stores the current run unreachable hosts in memory and if `skip_unreachable` is true, it will store them in `unavailable_hosts.csv` in mssh's private runtime directory: `$XDG_RUNTIME_DIR/multiSSH3`, else `/run/user/<uid>/multiSSH3`, `/dev/shm/multiSSH3-<uid>`, or a `multiSSH3-<uid>` folder in the system temp folder (the first that only you can write to). If none can be used, they are only kept for the current run.
|
|
829
|
+
- mssh stores the current run unreachable hosts in memory and if `skip_unreachable` is true, it will store them in `unavailable_hosts.csv` in mssh's private runtime directory: `$XDG_RUNTIME_DIR/multiSSH3`, else `/run/user/<uid>/multiSSH3`, `/dev/shm/multiSSH3-<uid>`, or a `multiSSH3-<uid>` folder in the system temp folder (the first that only you can write to, or could make). mssh reads them back from that one only, not from a later place an older session left them in. If none can be used, they are only kept for the current run.
|
|
828
830
|
- To force mssh to not use unreachable hosts from previous runs, you can use `-a, -nsu, --no_skip_unreachable` to set `skip_unreachable` to false.
|
|
829
831
|
|
|
830
832
|
### `-a, -nsu, --no_skip_unreachable` | not _`DEFAULT_SKIP_UNREACHABLE`_
|
|
@@ -972,7 +974,7 @@ Option names count in full only: `--user` is not `--username` and `-c` is not `-
|
|
|
972
974
|
|
|
973
975
|
Putting the command after `--` is no substitute for quotes: every word after the hosts is a command of its own, so `mssh web1 -- bash -c 'echo hi'` runs `bash`, `-c` and `echo hi` one after another (when each word is a single word, as in `mssh web1 -- ls -la`, mssh asks whether to join them).
|
|
974
976
|
|
|
975
|
-
Below is a sample help message output from multiSSH3 6.
|
|
977
|
+
Below is a sample help message output from multiSSH3 6.36 @ 2026-10-16 (no config file, 120 columns; the lines after the options list the programs found on that machine)
|
|
976
978
|
|
|
977
979
|
```bash
|
|
978
980
|
$ mssh -h
|
|
@@ -1003,7 +1005,7 @@ options:
|
|
|
1003
1005
|
The identity file to use to connect to the hosts. Implies --use_key. Specify a folder for program to search for a key. Use option without value to use ~/.ssh/ (default: None)
|
|
1004
1006
|
-uk, --use_key Attempt to use public key file to connect to the hosts. (default: False)
|
|
1005
1007
|
-ea, --extraargs EXTRAARGS
|
|
1006
|
-
Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments.
|
|
1008
|
+
Extra arguments to pass to the ssh / rsync / scp command. (Use -ia for ipmi arguments) Put in one string for multiple arguments. The word after -ea is its value, also when it starts with "-": quote an option with its value. Ex. -ea "-p 2222 -A" (default: None)
|
|
1007
1009
|
-11, --oneonone Run one corresponding command on each host. (default: False)
|
|
1008
1010
|
-f, --file FILE The file to be copied to the hosts. Use -f multiple times to copy multiple files
|
|
1009
1011
|
-s, -fs, --file_sync [FILE_SYNC]
|
|
@@ -1028,7 +1030,7 @@ options:
|
|
|
1028
1030
|
-ip, --ipmi_password IPMI_PASSWORD
|
|
1029
1031
|
The password to use to connect to the hosts via ipmi. (default: set, not shown)
|
|
1030
1032
|
-ia, --ipmi_args IPMI_ARGS
|
|
1031
|
-
The arguments passed to ipmitool to use to connect to the hosts via ipmi. (default: )
|
|
1033
|
+
The arguments passed to ipmitool to use to connect to the hosts via ipmi. The word after -ia is its value, also when it starts with "-": quote an option with its value. Ex. -ia "-I lanplus" (default: )
|
|
1032
1034
|
-im, --ipmi_method IPMI_METHOD
|
|
1033
1035
|
The method to use to connect to the hosts via ipmi. (default: ipmitool)
|
|
1034
1036
|
-irlt, --ipmi_retry_lockout_time IPMI_RETRY_LOCKOUT_TIME
|
|
@@ -83,6 +83,7 @@ tests/test_run_on_hosts.py
|
|
|
83
83
|
tests/test_run_on_hosts_modes.py
|
|
84
84
|
tests/test_runtime_branch_matrix.py
|
|
85
85
|
tests/test_runtime_dir.py
|
|
86
|
+
tests/test_shared_lines.py
|
|
86
87
|
tests/test_smoke_optional.py
|
|
87
88
|
tests/test_ssh_error_text.py
|
|
88
89
|
tests/test_ssh_timeouts.py
|
|
@@ -90,9 +91,11 @@ tests/test_sshpass_exit.py
|
|
|
90
91
|
tests/test_start_run_on_hosts.py
|
|
91
92
|
tests/test_stopped_commands.py
|
|
92
93
|
tests/test_stream_reader.py
|
|
94
|
+
tests/test_streamed_output.py
|
|
93
95
|
tests/test_tui_ssh_notices.py
|
|
94
96
|
tests/test_unavailable_hosts.py
|
|
95
97
|
tests/test_underscore_settings.py
|
|
98
|
+
tests/test_waiting.py
|
|
96
99
|
tests/test_wide_wrap.py
|
|
97
100
|
tests/vt_grid.py
|
|
98
101
|
tests/data/run_command_golden.json
|