multiSSH3 4.81__tar.gz → 4.83__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.
- {multissh3-4.81 → multissh3-4.83}/PKG-INFO +10 -38
- {multissh3-4.81 → multissh3-4.83}/README.md +9 -37
- {multissh3-4.81 → multissh3-4.83}/multiSSH3.egg-info/PKG-INFO +10 -38
- {multissh3-4.81 → multissh3-4.83}/multiSSH3.py +158 -73
- {multissh3-4.81 → multissh3-4.83}/setup.py +1 -1
- {multissh3-4.81 → multissh3-4.83}/LICENSE +0 -0
- {multissh3-4.81 → multissh3-4.83}/multiSSH3.egg-info/SOURCES.txt +0 -0
- {multissh3-4.81 → multissh3-4.83}/multiSSH3.egg-info/dependency_links.txt +0 -0
- {multissh3-4.81 → multissh3-4.83}/multiSSH3.egg-info/entry_points.txt +0 -0
- {multissh3-4.81 → multissh3-4.83}/multiSSH3.egg-info/requires.txt +0 -0
- {multissh3-4.81 → multissh3-4.83}/multiSSH3.egg-info/top_level.txt +0 -0
- {multissh3-4.81 → multissh3-4.83}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: multiSSH3
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.83
|
|
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
|
|
@@ -164,8 +164,6 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
164
164
|
- [multissh](#multissh)
|
|
165
165
|
- [Table of Contents](#table-of-contents)
|
|
166
166
|
- [Features](#features)
|
|
167
|
-
- [Installation](#installation)
|
|
168
|
-
- [Usage](#usage)
|
|
169
167
|
- [Basic Syntax](#basic-syntax)
|
|
170
168
|
- [Command-Line Options](#command-line-options)
|
|
171
169
|
- [Examples](#examples)
|
|
@@ -194,36 +192,10 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
194
192
|
- **Interactive Mode**: Run interactive commands with curses-based UI for monitoring.
|
|
195
193
|
- **Quiet Mode**: Suppress output for cleaner automation scripts.
|
|
196
194
|
|
|
197
|
-
## Installation
|
|
198
|
-
|
|
199
|
-
1. **Clone the Repository**
|
|
200
|
-
|
|
201
|
-
```bash
|
|
202
|
-
git clone https://github.com/yourusername/multissh.git
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
2. **Navigate to the Directory**
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
cd multissh
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
3. **Make the Script Executable**
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
chmod +x multissh.py
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
4. **Install Dependencies**
|
|
218
|
-
|
|
219
|
-
Ensure you have Python 3 and the required modules installed. You may need to install `curses` and `ipaddress` modules if they are not already available.
|
|
220
|
-
|
|
221
|
-
## Usage
|
|
222
|
-
|
|
223
195
|
### Basic Syntax
|
|
224
196
|
|
|
225
197
|
```bash
|
|
226
|
-
|
|
198
|
+
mssh [options] <hosts> <commands>
|
|
227
199
|
```
|
|
228
200
|
|
|
229
201
|
- `<hosts>`: Comma-separated list of target hosts. Supports ranges and wildcards.
|
|
@@ -267,7 +239,7 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
267
239
|
### Running a Command on Multiple Hosts
|
|
268
240
|
|
|
269
241
|
```bash
|
|
270
|
-
|
|
242
|
+
mssh "host1,host2,host3" "uptime"
|
|
271
243
|
```
|
|
272
244
|
|
|
273
245
|
This command runs `uptime` on `host1`, `host2`, and `host3`.
|
|
@@ -275,7 +247,7 @@ This command runs `uptime` on `host1`, `host2`, and `host3`.
|
|
|
275
247
|
### Copying Files to Multiple Hosts
|
|
276
248
|
|
|
277
249
|
```bash
|
|
278
|
-
|
|
250
|
+
mssh -f "/path/to/local/file.txt" "host1,host2,host3" "/remote/path/"
|
|
279
251
|
```
|
|
280
252
|
|
|
281
253
|
This command copies `file.txt` to `/remote/path/` on the specified hosts.
|
|
@@ -283,7 +255,7 @@ This command copies `file.txt` to `/remote/path/` on the specified hosts.
|
|
|
283
255
|
### Using Hostname Ranges
|
|
284
256
|
|
|
285
257
|
```bash
|
|
286
|
-
|
|
258
|
+
mssh "host[01-05]" "hostname"
|
|
287
259
|
```
|
|
288
260
|
|
|
289
261
|
This expands to `host01`, `host02`, `host03`, `host04`, `host05` and runs `hostname` on each.
|
|
@@ -291,7 +263,7 @@ This expands to `host01`, `host02`, `host03`, `host04`, `host05` and runs `hostn
|
|
|
291
263
|
### Using IPMI
|
|
292
264
|
|
|
293
265
|
```bash
|
|
294
|
-
|
|
266
|
+
mssh --ipmi "192.168.1.[100-105]" "chassis power status"
|
|
295
267
|
```
|
|
296
268
|
|
|
297
269
|
Runs `ipmitool chassis power status` on the specified IPMI interfaces.
|
|
@@ -299,7 +271,7 @@ Runs `ipmitool chassis power status` on the specified IPMI interfaces.
|
|
|
299
271
|
### Using Password Authentication
|
|
300
272
|
|
|
301
273
|
```bash
|
|
302
|
-
|
|
274
|
+
mssh -p "yourpassword" "host1,host2" "whoami"
|
|
303
275
|
```
|
|
304
276
|
|
|
305
277
|
Uses `sshpass` to provide the password for SSH authentication.
|
|
@@ -307,7 +279,7 @@ Uses `sshpass` to provide the password for SSH authentication.
|
|
|
307
279
|
### Skipping Unreachable Hosts
|
|
308
280
|
|
|
309
281
|
```bash
|
|
310
|
-
|
|
282
|
+
mssh -su "host1,host2,host3" "date"
|
|
311
283
|
```
|
|
312
284
|
|
|
313
285
|
Skips hosts that are unreachable during execution.
|
|
@@ -315,7 +287,7 @@ Skips hosts that are unreachable during execution.
|
|
|
315
287
|
### JSON Output
|
|
316
288
|
|
|
317
289
|
```bash
|
|
318
|
-
|
|
290
|
+
mssh -j "host1,host2" "uname -a"
|
|
319
291
|
```
|
|
320
292
|
|
|
321
293
|
Outputs the results in JSON format, suitable for parsing.
|
|
@@ -323,7 +295,7 @@ Outputs the results in JSON format, suitable for parsing.
|
|
|
323
295
|
### Quiet Mode
|
|
324
296
|
|
|
325
297
|
```bash
|
|
326
|
-
|
|
298
|
+
mssh -q "host1,host2" "ls /nonexistent"
|
|
327
299
|
```
|
|
328
300
|
|
|
329
301
|
Suppresses all output, useful for scripts where you only care about exit codes.
|
|
@@ -148,8 +148,6 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
148
148
|
- [multissh](#multissh)
|
|
149
149
|
- [Table of Contents](#table-of-contents)
|
|
150
150
|
- [Features](#features)
|
|
151
|
-
- [Installation](#installation)
|
|
152
|
-
- [Usage](#usage)
|
|
153
151
|
- [Basic Syntax](#basic-syntax)
|
|
154
152
|
- [Command-Line Options](#command-line-options)
|
|
155
153
|
- [Examples](#examples)
|
|
@@ -178,36 +176,10 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
178
176
|
- **Interactive Mode**: Run interactive commands with curses-based UI for monitoring.
|
|
179
177
|
- **Quiet Mode**: Suppress output for cleaner automation scripts.
|
|
180
178
|
|
|
181
|
-
## Installation
|
|
182
|
-
|
|
183
|
-
1. **Clone the Repository**
|
|
184
|
-
|
|
185
|
-
```bash
|
|
186
|
-
git clone https://github.com/yourusername/multissh.git
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
2. **Navigate to the Directory**
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
cd multissh
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
3. **Make the Script Executable**
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
|
-
chmod +x multissh.py
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
4. **Install Dependencies**
|
|
202
|
-
|
|
203
|
-
Ensure you have Python 3 and the required modules installed. You may need to install `curses` and `ipaddress` modules if they are not already available.
|
|
204
|
-
|
|
205
|
-
## Usage
|
|
206
|
-
|
|
207
179
|
### Basic Syntax
|
|
208
180
|
|
|
209
181
|
```bash
|
|
210
|
-
|
|
182
|
+
mssh [options] <hosts> <commands>
|
|
211
183
|
```
|
|
212
184
|
|
|
213
185
|
- `<hosts>`: Comma-separated list of target hosts. Supports ranges and wildcards.
|
|
@@ -251,7 +223,7 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
251
223
|
### Running a Command on Multiple Hosts
|
|
252
224
|
|
|
253
225
|
```bash
|
|
254
|
-
|
|
226
|
+
mssh "host1,host2,host3" "uptime"
|
|
255
227
|
```
|
|
256
228
|
|
|
257
229
|
This command runs `uptime` on `host1`, `host2`, and `host3`.
|
|
@@ -259,7 +231,7 @@ This command runs `uptime` on `host1`, `host2`, and `host3`.
|
|
|
259
231
|
### Copying Files to Multiple Hosts
|
|
260
232
|
|
|
261
233
|
```bash
|
|
262
|
-
|
|
234
|
+
mssh -f "/path/to/local/file.txt" "host1,host2,host3" "/remote/path/"
|
|
263
235
|
```
|
|
264
236
|
|
|
265
237
|
This command copies `file.txt` to `/remote/path/` on the specified hosts.
|
|
@@ -267,7 +239,7 @@ This command copies `file.txt` to `/remote/path/` on the specified hosts.
|
|
|
267
239
|
### Using Hostname Ranges
|
|
268
240
|
|
|
269
241
|
```bash
|
|
270
|
-
|
|
242
|
+
mssh "host[01-05]" "hostname"
|
|
271
243
|
```
|
|
272
244
|
|
|
273
245
|
This expands to `host01`, `host02`, `host03`, `host04`, `host05` and runs `hostname` on each.
|
|
@@ -275,7 +247,7 @@ This expands to `host01`, `host02`, `host03`, `host04`, `host05` and runs `hostn
|
|
|
275
247
|
### Using IPMI
|
|
276
248
|
|
|
277
249
|
```bash
|
|
278
|
-
|
|
250
|
+
mssh --ipmi "192.168.1.[100-105]" "chassis power status"
|
|
279
251
|
```
|
|
280
252
|
|
|
281
253
|
Runs `ipmitool chassis power status` on the specified IPMI interfaces.
|
|
@@ -283,7 +255,7 @@ Runs `ipmitool chassis power status` on the specified IPMI interfaces.
|
|
|
283
255
|
### Using Password Authentication
|
|
284
256
|
|
|
285
257
|
```bash
|
|
286
|
-
|
|
258
|
+
mssh -p "yourpassword" "host1,host2" "whoami"
|
|
287
259
|
```
|
|
288
260
|
|
|
289
261
|
Uses `sshpass` to provide the password for SSH authentication.
|
|
@@ -291,7 +263,7 @@ Uses `sshpass` to provide the password for SSH authentication.
|
|
|
291
263
|
### Skipping Unreachable Hosts
|
|
292
264
|
|
|
293
265
|
```bash
|
|
294
|
-
|
|
266
|
+
mssh -su "host1,host2,host3" "date"
|
|
295
267
|
```
|
|
296
268
|
|
|
297
269
|
Skips hosts that are unreachable during execution.
|
|
@@ -299,7 +271,7 @@ Skips hosts that are unreachable during execution.
|
|
|
299
271
|
### JSON Output
|
|
300
272
|
|
|
301
273
|
```bash
|
|
302
|
-
|
|
274
|
+
mssh -j "host1,host2" "uname -a"
|
|
303
275
|
```
|
|
304
276
|
|
|
305
277
|
Outputs the results in JSON format, suitable for parsing.
|
|
@@ -307,7 +279,7 @@ Outputs the results in JSON format, suitable for parsing.
|
|
|
307
279
|
### Quiet Mode
|
|
308
280
|
|
|
309
281
|
```bash
|
|
310
|
-
|
|
282
|
+
mssh -q "host1,host2" "ls /nonexistent"
|
|
311
283
|
```
|
|
312
284
|
|
|
313
285
|
Suppresses all output, useful for scripts where you only care about exit codes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: multiSSH3
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.83
|
|
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
|
|
@@ -164,8 +164,6 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
164
164
|
- [multissh](#multissh)
|
|
165
165
|
- [Table of Contents](#table-of-contents)
|
|
166
166
|
- [Features](#features)
|
|
167
|
-
- [Installation](#installation)
|
|
168
|
-
- [Usage](#usage)
|
|
169
167
|
- [Basic Syntax](#basic-syntax)
|
|
170
168
|
- [Command-Line Options](#command-line-options)
|
|
171
169
|
- [Examples](#examples)
|
|
@@ -194,36 +192,10 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
194
192
|
- **Interactive Mode**: Run interactive commands with curses-based UI for monitoring.
|
|
195
193
|
- **Quiet Mode**: Suppress output for cleaner automation scripts.
|
|
196
194
|
|
|
197
|
-
## Installation
|
|
198
|
-
|
|
199
|
-
1. **Clone the Repository**
|
|
200
|
-
|
|
201
|
-
```bash
|
|
202
|
-
git clone https://github.com/yourusername/multissh.git
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
2. **Navigate to the Directory**
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
cd multissh
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
3. **Make the Script Executable**
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
chmod +x multissh.py
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
4. **Install Dependencies**
|
|
218
|
-
|
|
219
|
-
Ensure you have Python 3 and the required modules installed. You may need to install `curses` and `ipaddress` modules if they are not already available.
|
|
220
|
-
|
|
221
|
-
## Usage
|
|
222
|
-
|
|
223
195
|
### Basic Syntax
|
|
224
196
|
|
|
225
197
|
```bash
|
|
226
|
-
|
|
198
|
+
mssh [options] <hosts> <commands>
|
|
227
199
|
```
|
|
228
200
|
|
|
229
201
|
- `<hosts>`: Comma-separated list of target hosts. Supports ranges and wildcards.
|
|
@@ -267,7 +239,7 @@ Following document is generated courtesy of Mr.ChatGPT-o1 Preview:
|
|
|
267
239
|
### Running a Command on Multiple Hosts
|
|
268
240
|
|
|
269
241
|
```bash
|
|
270
|
-
|
|
242
|
+
mssh "host1,host2,host3" "uptime"
|
|
271
243
|
```
|
|
272
244
|
|
|
273
245
|
This command runs `uptime` on `host1`, `host2`, and `host3`.
|
|
@@ -275,7 +247,7 @@ This command runs `uptime` on `host1`, `host2`, and `host3`.
|
|
|
275
247
|
### Copying Files to Multiple Hosts
|
|
276
248
|
|
|
277
249
|
```bash
|
|
278
|
-
|
|
250
|
+
mssh -f "/path/to/local/file.txt" "host1,host2,host3" "/remote/path/"
|
|
279
251
|
```
|
|
280
252
|
|
|
281
253
|
This command copies `file.txt` to `/remote/path/` on the specified hosts.
|
|
@@ -283,7 +255,7 @@ This command copies `file.txt` to `/remote/path/` on the specified hosts.
|
|
|
283
255
|
### Using Hostname Ranges
|
|
284
256
|
|
|
285
257
|
```bash
|
|
286
|
-
|
|
258
|
+
mssh "host[01-05]" "hostname"
|
|
287
259
|
```
|
|
288
260
|
|
|
289
261
|
This expands to `host01`, `host02`, `host03`, `host04`, `host05` and runs `hostname` on each.
|
|
@@ -291,7 +263,7 @@ This expands to `host01`, `host02`, `host03`, `host04`, `host05` and runs `hostn
|
|
|
291
263
|
### Using IPMI
|
|
292
264
|
|
|
293
265
|
```bash
|
|
294
|
-
|
|
266
|
+
mssh --ipmi "192.168.1.[100-105]" "chassis power status"
|
|
295
267
|
```
|
|
296
268
|
|
|
297
269
|
Runs `ipmitool chassis power status` on the specified IPMI interfaces.
|
|
@@ -299,7 +271,7 @@ Runs `ipmitool chassis power status` on the specified IPMI interfaces.
|
|
|
299
271
|
### Using Password Authentication
|
|
300
272
|
|
|
301
273
|
```bash
|
|
302
|
-
|
|
274
|
+
mssh -p "yourpassword" "host1,host2" "whoami"
|
|
303
275
|
```
|
|
304
276
|
|
|
305
277
|
Uses `sshpass` to provide the password for SSH authentication.
|
|
@@ -307,7 +279,7 @@ Uses `sshpass` to provide the password for SSH authentication.
|
|
|
307
279
|
### Skipping Unreachable Hosts
|
|
308
280
|
|
|
309
281
|
```bash
|
|
310
|
-
|
|
282
|
+
mssh -su "host1,host2,host3" "date"
|
|
311
283
|
```
|
|
312
284
|
|
|
313
285
|
Skips hosts that are unreachable during execution.
|
|
@@ -315,7 +287,7 @@ Skips hosts that are unreachable during execution.
|
|
|
315
287
|
### JSON Output
|
|
316
288
|
|
|
317
289
|
```bash
|
|
318
|
-
|
|
290
|
+
mssh -j "host1,host2" "uname -a"
|
|
319
291
|
```
|
|
320
292
|
|
|
321
293
|
Outputs the results in JSON format, suitable for parsing.
|
|
@@ -323,7 +295,7 @@ Outputs the results in JSON format, suitable for parsing.
|
|
|
323
295
|
### Quiet Mode
|
|
324
296
|
|
|
325
297
|
```bash
|
|
326
|
-
|
|
298
|
+
mssh -q "host1,host2" "ls /nonexistent"
|
|
327
299
|
```
|
|
328
300
|
|
|
329
301
|
Suppresses all output, useful for scripts where you only care about exit codes.
|
|
@@ -15,7 +15,9 @@ import io
|
|
|
15
15
|
import signal
|
|
16
16
|
import functools
|
|
17
17
|
import glob
|
|
18
|
-
|
|
18
|
+
import shutil
|
|
19
|
+
import getpass
|
|
20
|
+
|
|
19
21
|
try:
|
|
20
22
|
# Check if functiools.cache is available
|
|
21
23
|
cache_decorator = functools.cache
|
|
@@ -27,7 +29,7 @@ except AttributeError:
|
|
|
27
29
|
# If neither is available, use a dummy decorator
|
|
28
30
|
def cache_decorator(func):
|
|
29
31
|
return func
|
|
30
|
-
version = '4.
|
|
32
|
+
version = '4.83'
|
|
31
33
|
VERSION = version
|
|
32
34
|
|
|
33
35
|
CONFIG_FILE = '/etc/multiSSH3.config.json'
|
|
@@ -87,13 +89,20 @@ __build_in_default_config = {
|
|
|
87
89
|
'Warning: Permanently added',
|
|
88
90
|
'mux_client_request_session',
|
|
89
91
|
'disabling multiplexing',
|
|
92
|
+
'Killed by signal',
|
|
93
|
+
'Connection reset by peer',
|
|
90
94
|
],
|
|
91
95
|
'_DEFAULT_CALLED': True,
|
|
92
96
|
'_DEFAULT_RETURN_UNFINISHED': False,
|
|
93
97
|
'_DEFAULT_UPDATE_UNREACHABLE_HOSTS': True,
|
|
94
98
|
'_DEFAULT_NO_START': False,
|
|
95
99
|
'_etc_hosts': {},
|
|
96
|
-
'
|
|
100
|
+
'_sshpassPath': None,
|
|
101
|
+
'_sshPath': None,
|
|
102
|
+
'_scpPath': None,
|
|
103
|
+
'_ipmitoolPath': None,
|
|
104
|
+
'_rsyncPath': None,
|
|
105
|
+
'_bashPath': None,
|
|
97
106
|
'__ERROR_MESSAGES_TO_IGNORE_REGEX':None,
|
|
98
107
|
}
|
|
99
108
|
|
|
@@ -139,7 +148,8 @@ _DEFAULT_UPDATE_UNREACHABLE_HOSTS = __configs_from_file.get('_DEFAULT_UPDATE_UNR
|
|
|
139
148
|
_DEFAULT_NO_START = __configs_from_file.get('_DEFAULT_NO_START', __build_in_default_config['_DEFAULT_NO_START'])
|
|
140
149
|
|
|
141
150
|
# form the regex from the list
|
|
142
|
-
|
|
151
|
+
__ERROR_MESSAGES_TO_IGNORE_REGEX = __configs_from_file.get('__ERROR_MESSAGES_TO_IGNORE_REGEX', __build_in_default_config['__ERROR_MESSAGES_TO_IGNORE_REGEX'])
|
|
152
|
+
if __ERROR_MESSAGES_TO_IGNORE_REGEX:
|
|
143
153
|
print('Using __ERROR_MESSAGES_TO_IGNORE_REGEX from config file, ignoring ERROR_MESSAGES_TO_IGNORE')
|
|
144
154
|
__ERROR_MESSAGES_TO_IGNORE_REGEX = re.compile(__configs_from_file['__ERROR_MESSAGES_TO_IGNORE_REGEX'])
|
|
145
155
|
else:
|
|
@@ -152,7 +162,7 @@ __global_suppress_printout = True
|
|
|
152
162
|
__mainReturnCode = 0
|
|
153
163
|
__failedHosts = set()
|
|
154
164
|
class Host:
|
|
155
|
-
def __init__(self, name, command, files = None,ipmi = False,interface_ip_prefix = None,scp=False,extraargs=None):
|
|
165
|
+
def __init__(self, name, command, files = None,ipmi = False,interface_ip_prefix = None,scp=False,extraargs=None,gatherMode=False):
|
|
156
166
|
self.name = name # the name of the host (hostname or IP address)
|
|
157
167
|
self.command = command # the command to run on the host
|
|
158
168
|
self.returncode = None # the return code of the command
|
|
@@ -164,13 +174,14 @@ class Host:
|
|
|
164
174
|
self.ipmi = ipmi # whether to use ipmi to connect to the host
|
|
165
175
|
self.interface_ip_prefix = interface_ip_prefix # the prefix of the ip address of the interface to be used to connect to the host
|
|
166
176
|
self.scp = scp # whether to use scp to copy files to the host
|
|
177
|
+
self.gatherMode = gatherMode # whether the host is in gather mode
|
|
167
178
|
self.extraargs = extraargs # extra arguments to be passed to ssh
|
|
168
179
|
self.resolvedName = None # the resolved IP address of the host
|
|
169
180
|
def __iter__(self):
|
|
170
181
|
return zip(['name', 'command', 'returncode', 'stdout', 'stderr'], [self.name, self.command, self.returncode, self.stdout, self.stderr])
|
|
171
182
|
def __repr__(self):
|
|
172
183
|
# return the complete data structure
|
|
173
|
-
return f"Host(name={self.name}, command={self.command}, returncode={self.returncode}, stdout={self.stdout}, stderr={self.stderr}, output={self.output}, printedLines={self.printedLines}, files={self.files}, ipmi={self.ipmi}, interface_ip_prefix={self.interface_ip_prefix}, scp={self.scp}, extraargs={self.extraargs}, resolvedName={self.resolvedName})"
|
|
184
|
+
return f"Host(name={self.name}, command={self.command}, returncode={self.returncode}, stdout={self.stdout}, stderr={self.stderr}, output={self.output}, printedLines={self.printedLines}, files={self.files}, ipmi={self.ipmi}, interface_ip_prefix={self.interface_ip_prefix}, scp={self.scp}, gatherMode={self.gatherMode}, extraargs={self.extraargs}, resolvedName={self.resolvedName})"
|
|
174
185
|
def __str__(self):
|
|
175
186
|
return f"Host(name={self.name}, command={self.command}, returncode={self.returncode}, stdout={self.stdout}, stderr={self.stderr})"
|
|
176
187
|
|
|
@@ -189,12 +200,24 @@ _etc_hosts = __configs_from_file.get('_etc_hosts', __build_in_default_config['_e
|
|
|
189
200
|
_env_file = DEFAULT_ENV_FILE
|
|
190
201
|
|
|
191
202
|
# check if command sshpass is available
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
_binPaths = {}
|
|
204
|
+
def check_path(program_name):
|
|
205
|
+
global __configs_from_file
|
|
206
|
+
global __build_in_default_config
|
|
207
|
+
global _binPaths
|
|
208
|
+
config_key = f'_{program_name}Path'
|
|
209
|
+
program_path = (
|
|
210
|
+
__configs_from_file.get(config_key) or
|
|
211
|
+
__build_in_default_config.get(config_key) or
|
|
212
|
+
shutil.which(program_name)
|
|
213
|
+
)
|
|
214
|
+
if program_path:
|
|
215
|
+
_binPaths[program_name] = program_path
|
|
216
|
+
return True
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
[check_path(program) for program in ['sshpass', 'ssh', 'scp', 'ipmitool','rsync','bash']]
|
|
220
|
+
|
|
198
221
|
|
|
199
222
|
|
|
200
223
|
@cache_decorator
|
|
@@ -582,64 +605,124 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
582
605
|
global _emo
|
|
583
606
|
global __ERROR_MESSAGES_TO_IGNORE_REGEX
|
|
584
607
|
global __ipmiiInterfaceIPPrefix
|
|
585
|
-
global
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
if
|
|
595
|
-
|
|
596
|
-
host.command = host.command.replace("#USER#",host.username).replace("#USERNAME#",host.username).replace("#user#",host.username).replace("#username#",host.username)
|
|
597
|
-
else:
|
|
598
|
-
host.command = host.command.replace("#USER#",'CURRENT_USER').replace("#USERNAME#",'CURRENT_USER').replace("#user#",'CURRENT_USER').replace("#username#",'CURRENT_USER')
|
|
599
|
-
formatedCMD = []
|
|
600
|
-
if host.extraargs:
|
|
601
|
-
extraargs = host.extraargs.split()
|
|
602
|
-
else:
|
|
603
|
-
extraargs = []
|
|
604
|
-
if __ipmiiInterfaceIPPrefix:
|
|
605
|
-
host.interface_ip_prefix = __ipmiiInterfaceIPPrefix if host.ipmi and not host.interface_ip_prefix else host.interface_ip_prefix
|
|
606
|
-
if host.interface_ip_prefix:
|
|
607
|
-
try:
|
|
608
|
-
hostOctets = getIP(host.address,local=False).split('.')
|
|
609
|
-
prefixOctets = host.interface_ip_prefix.split('.')
|
|
610
|
-
host.address = '.'.join(prefixOctets[:3]+hostOctets[min(3,len(prefixOctets)):])
|
|
611
|
-
host.resolvedName = host.username + '@' if host.username else ''
|
|
612
|
-
host.resolvedName += host.address
|
|
613
|
-
except:
|
|
614
|
-
host.resolvedName = host.name
|
|
608
|
+
global _binPaths
|
|
609
|
+
try:
|
|
610
|
+
host.username = None
|
|
611
|
+
host.address = host.name
|
|
612
|
+
if '@' in host.name:
|
|
613
|
+
host.username, host.address = host.name.rsplit('@',1)
|
|
614
|
+
if "#HOST#" in host.command.upper() or "#HOSTNAME#" in host.command.upper():
|
|
615
|
+
host.command = host.command.replace("#HOST#",host.address).replace("#HOSTNAME#",host.address).replace("#host#",host.address).replace("#hostname#",host.address)
|
|
616
|
+
if "#USER#" in host.command.upper() or "#USERNAME#" in host.command.upper():
|
|
617
|
+
if host.username:
|
|
618
|
+
host.command = host.command.replace("#USER#",host.username).replace("#USERNAME#",host.username).replace("#user#",host.username).replace("#username#",host.username)
|
|
615
619
|
else:
|
|
620
|
+
current_user = getpass.getuser()
|
|
621
|
+
host.command = host.command.replace("#USER#",current_user).replace("#USERNAME#",current_user).replace("#user#",current_user).replace("#username#",current_user)
|
|
622
|
+
formatedCMD = []
|
|
623
|
+
if host.extraargs and type(host.extraargs) == str:
|
|
624
|
+
extraargs = host.extraargs.split()
|
|
625
|
+
elif host.extraargs and type(host.extraargs) == list:
|
|
626
|
+
extraargs = [str(arg) for arg in host.extraargs]
|
|
627
|
+
else:
|
|
628
|
+
extraargs = []
|
|
629
|
+
if __ipmiiInterfaceIPPrefix:
|
|
630
|
+
host.interface_ip_prefix = __ipmiiInterfaceIPPrefix if host.ipmi and not host.interface_ip_prefix else host.interface_ip_prefix
|
|
631
|
+
if host.interface_ip_prefix:
|
|
632
|
+
try:
|
|
633
|
+
hostOctets = getIP(host.address,local=False).split('.')
|
|
634
|
+
prefixOctets = host.interface_ip_prefix.split('.')
|
|
635
|
+
host.address = '.'.join(prefixOctets[:3]+hostOctets[min(3,len(prefixOctets)):])
|
|
636
|
+
host.resolvedName = host.username + '@' if host.username else ''
|
|
637
|
+
host.resolvedName += host.address
|
|
638
|
+
except:
|
|
616
639
|
host.resolvedName = host.name
|
|
617
|
-
|
|
640
|
+
else:
|
|
641
|
+
host.resolvedName = host.name
|
|
642
|
+
if host.ipmi:
|
|
643
|
+
if 'ipmitool' in _binPaths:
|
|
618
644
|
if host.command.startswith('ipmitool '):
|
|
619
645
|
host.command = host.command.replace('ipmitool ','')
|
|
646
|
+
elif host.command.startswith(_binPaths['ipmitool']):
|
|
647
|
+
host.command = host.command.replace(_binPaths['ipmitool'],'')
|
|
620
648
|
if not host.username:
|
|
621
649
|
host.username = 'admin'
|
|
622
|
-
if
|
|
623
|
-
|
|
650
|
+
if 'bash' in _binPaths:
|
|
651
|
+
if passwds:
|
|
652
|
+
formatedCMD = [_binPaths['bash'],'-c',f'ipmitool -H {host.address} -U {host.username} -P {passwds} {" ".join(extraargs)} {host.command}']
|
|
653
|
+
else:
|
|
654
|
+
formatedCMD = [_binPaths['bash'],'-c',f'ipmitool -H {host.address} -U {host.username} {" ".join(extraargs)} {host.command}']
|
|
624
655
|
else:
|
|
625
|
-
|
|
656
|
+
if passwds:
|
|
657
|
+
formatedCMD = [_binPaths['ipmitool'],f'-H {host.address}',f'-U {host.username}',f'-P {passwds}'] + extraargs + [host.command]
|
|
658
|
+
else:
|
|
659
|
+
formatedCMD = [_binPaths['ipmitool'],f'-H {host.address}',f'-U {host.username}'] + extraargs + [host.command]
|
|
660
|
+
elif 'ssh' in _binPaths:
|
|
661
|
+
host.output.append('Ipmitool not found on the local machine! Trying ipmitool on the remote machine...')
|
|
662
|
+
host.ipmi = False
|
|
663
|
+
host.interface_ip_prefix = None
|
|
664
|
+
host.command = 'ipmitool '+host.command if not host.command.startswith('ipmitool ') else host.command
|
|
665
|
+
ssh_command(host,sem,timeout,passwds)
|
|
666
|
+
return
|
|
626
667
|
else:
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
668
|
+
host.output.append('Ipmitool not found on the local machine! Please install ipmitool to use ipmi mode.')
|
|
669
|
+
host.stderr.append('Ipmitool not found on the local machine! Please install ipmitool to use ipmi mode.')
|
|
670
|
+
host.returncode = 1
|
|
671
|
+
return
|
|
672
|
+
else:
|
|
673
|
+
if host.files:
|
|
674
|
+
if host.scp:
|
|
675
|
+
if 'scp' in _binPaths:
|
|
676
|
+
useScp = True
|
|
677
|
+
elif 'rsync' in _binPaths:
|
|
678
|
+
host.output.append('scp not found on the local machine! Trying to use rsync...')
|
|
679
|
+
useScp = False
|
|
630
680
|
else:
|
|
631
|
-
|
|
681
|
+
host.output.append('scp not found on the local machine! Please install scp or rsync to use file sync mode.')
|
|
682
|
+
host.stderr.append('scp not found on the local machine! Please install scp or rsync to use file sync mode.')
|
|
683
|
+
host.returncode = 1
|
|
684
|
+
return
|
|
685
|
+
elif 'rsync' in _binPaths:
|
|
686
|
+
useScp = False
|
|
687
|
+
elif 'scp' in _binPaths:
|
|
688
|
+
host.output.append('rsync not found on the local machine! Trying to use scp...')
|
|
689
|
+
useScp = True
|
|
690
|
+
else:
|
|
691
|
+
host.output.append('rsync not found on the local machine! Please install rsync or scp to use file sync mode.')
|
|
692
|
+
host.stderr.append('rsync not found on the local machine! Please install rsync or scp to use file sync mode.')
|
|
693
|
+
host.returncode = 1
|
|
694
|
+
return
|
|
695
|
+
if host.gatherMode:
|
|
696
|
+
fileArgs = [f'{host.resolvedName}:{file}' for file in host.files] + [host.command]
|
|
632
697
|
else:
|
|
633
|
-
|
|
634
|
-
if
|
|
635
|
-
formatedCMD = ['
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
698
|
+
fileArgs = host.files + [f'{host.resolvedName}:{host.command}']
|
|
699
|
+
if useScp:
|
|
700
|
+
formatedCMD = [_binPaths['scp'],'-rpB'] + extraargs +['--']+fileArgs
|
|
701
|
+
else:
|
|
702
|
+
formatedCMD = [_binPaths['rsync'],'-ahlX','--partial','--inplace', '--info=name'] + extraargs +['--']+fileArgs
|
|
703
|
+
else:
|
|
704
|
+
formatedCMD = [_binPaths['ssh']] + extraargs +['--']+ [host.resolvedName, host.command]
|
|
705
|
+
if passwds and 'sshpass' in _binPaths:
|
|
706
|
+
formatedCMD = [_binPaths['sshpass'], '-p', passwds] + formatedCMD
|
|
707
|
+
elif passwds:
|
|
708
|
+
host.output.append('Warning: sshpass is not available. Please install sshpass to use password authentication.')
|
|
709
|
+
#host.stderr.append('Warning: sshpass is not available. Please install sshpass to use password authentication.')
|
|
710
|
+
host.output.append('Please provide password via live input or use ssh key authentication.')
|
|
711
|
+
# # try to send the password via __keyPressesIn
|
|
712
|
+
# __keyPressesIn[-1] = list(passwds) + ['\n']
|
|
713
|
+
# __keyPressesIn.append([])
|
|
714
|
+
except Exception as e:
|
|
715
|
+
import traceback
|
|
716
|
+
host.output.append(f'Error occurred while formatting the command : {host.command}!')
|
|
717
|
+
host.stderr.append(f'Error occurred while formatting the command : {host.command}!')
|
|
718
|
+
host.stderr.extend(str(e).split('\n'))
|
|
719
|
+
host.output.extend(str(e).split('\n'))
|
|
720
|
+
host.stderr.extend(traceback.format_exc().split('\n'))
|
|
721
|
+
host.output.extend(traceback.format_exc().split('\n'))
|
|
722
|
+
host.returncode = -1
|
|
723
|
+
return
|
|
724
|
+
with sem:
|
|
725
|
+
try:
|
|
643
726
|
host.output.append('Running command: '+' '.join(formatedCMD))
|
|
644
727
|
#host.stdout = []
|
|
645
728
|
proc = subprocess.Popen(formatedCMD,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE)
|
|
@@ -733,7 +816,7 @@ def ssh_command(host, sem, timeout=60,passwds=None):
|
|
|
733
816
|
host.command = 'ipmitool '+host.command if not host.command.startswith('ipmitool ') else host.command
|
|
734
817
|
ssh_command(host,sem,timeout,passwds)
|
|
735
818
|
# If transfering files, we will try again using scp if rsync connection is not successful
|
|
736
|
-
if host.files and not host.scp and host.returncode != 0 and host.stderr:
|
|
819
|
+
if host.files and not host.scp and not useScp and host.returncode != 0 and host.stderr:
|
|
737
820
|
host.stderr = []
|
|
738
821
|
host.stdout = []
|
|
739
822
|
host.output.append('Rsync connection failed! Trying SCP connection...')
|
|
@@ -1242,7 +1325,7 @@ def formHostStr(host) -> str:
|
|
|
1242
1325
|
def __formCommandArgStr(oneonone = DEFAULT_ONE_ON_ONE, timeout = DEFAULT_TIMEOUT,password = DEFAULT_PASSWORD,
|
|
1243
1326
|
nowatch = DEFAULT_NO_WATCH,json = DEFAULT_JSON_MODE,max_connections=DEFAULT_MAX_CONNECTIONS,
|
|
1244
1327
|
files = None,ipmi = DEFAULT_IPMI,interface_ip_prefix = DEFAULT_INTERFACE_IP_PREFIX,
|
|
1245
|
-
scp=DEFAULT_SCP,username=DEFAULT_USERNAME,extraargs=DEFAULT_EXTRA_ARGS,skipUnreachable=DEFAULT_SKIP_UNREACHABLE,
|
|
1328
|
+
scp=DEFAULT_SCP,gather_mode = False,username=DEFAULT_USERNAME,extraargs=DEFAULT_EXTRA_ARGS,skipUnreachable=DEFAULT_SKIP_UNREACHABLE,
|
|
1246
1329
|
no_env=DEFAULT_NO_ENV,greppable=DEFAULT_GREPPABLE_MODE,skip_hosts = DEFAULT_SKIP_HOSTS,
|
|
1247
1330
|
file_sync = False, error_only = DEFAULT_ERROR_ONLY,
|
|
1248
1331
|
shortend = False) -> str:
|
|
@@ -1257,6 +1340,7 @@ def __formCommandArgStr(oneonone = DEFAULT_ONE_ON_ONE, timeout = DEFAULT_TIMEOUT
|
|
|
1257
1340
|
if ipmi: argsList.append('--ipmi')
|
|
1258
1341
|
if interface_ip_prefix and interface_ip_prefix != DEFAULT_INTERFACE_IP_PREFIX: argsList.append(f'--interface_ip_prefix="{interface_ip_prefix}"' if not shortend else f'-pre="{interface_ip_prefix}"')
|
|
1259
1342
|
if scp: argsList.append('--scp')
|
|
1343
|
+
if gather_mode: argsList.append('--gather_mode' if not shortend else '-gm')
|
|
1260
1344
|
if username and username != DEFAULT_USERNAME: argsList.append(f'--username="{username}"' if not shortend else f'-u="{username}"')
|
|
1261
1345
|
if extraargs and extraargs != DEFAULT_EXTRA_ARGS: argsList.append(f'--extraargs="{extraargs}"' if not shortend else f'-ea="{extraargs}"')
|
|
1262
1346
|
if skipUnreachable: argsList.append('--skipUnreachable' if not shortend else '-su')
|
|
@@ -1270,7 +1354,7 @@ def __formCommandArgStr(oneonone = DEFAULT_ONE_ON_ONE, timeout = DEFAULT_TIMEOUT
|
|
|
1270
1354
|
def getStrCommand(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAULT_ONE_ON_ONE, timeout = DEFAULT_TIMEOUT,password = DEFAULT_PASSWORD,
|
|
1271
1355
|
nowatch = DEFAULT_NO_WATCH,json = DEFAULT_JSON_MODE,called = _DEFAULT_CALLED,max_connections=DEFAULT_MAX_CONNECTIONS,
|
|
1272
1356
|
files = None,ipmi = DEFAULT_IPMI,interface_ip_prefix = DEFAULT_INTERFACE_IP_PREFIX,returnUnfinished = _DEFAULT_RETURN_UNFINISHED,
|
|
1273
|
-
scp=DEFAULT_SCP,username=DEFAULT_USERNAME,extraargs=DEFAULT_EXTRA_ARGS,skipUnreachable=DEFAULT_SKIP_UNREACHABLE,
|
|
1357
|
+
scp=DEFAULT_SCP,gather_mode = False,username=DEFAULT_USERNAME,extraargs=DEFAULT_EXTRA_ARGS,skipUnreachable=DEFAULT_SKIP_UNREACHABLE,
|
|
1274
1358
|
no_env=DEFAULT_NO_ENV,greppable=DEFAULT_GREPPABLE_MODE,willUpdateUnreachableHosts=_DEFAULT_UPDATE_UNREACHABLE_HOSTS,no_start=_DEFAULT_NO_START,
|
|
1275
1359
|
skip_hosts = DEFAULT_SKIP_HOSTS, curses_min_char_len = DEFAULT_CURSES_MINIMUM_CHAR_LEN, curses_min_line_len = DEFAULT_CURSES_MINIMUM_LINE_LEN,
|
|
1276
1360
|
single_window = DEFAULT_SINGLE_WINDOW,file_sync = False,error_only = DEFAULT_ERROR_ONLY, shortend = False):
|
|
@@ -1279,7 +1363,7 @@ def getStrCommand(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAULT_ONE_O
|
|
|
1279
1363
|
files = frozenset(files) if files else None
|
|
1280
1364
|
argsStr = __formCommandArgStr(oneonone = oneonone, timeout = timeout,password = password,
|
|
1281
1365
|
nowatch = nowatch,json = json,max_connections=max_connections,
|
|
1282
|
-
files = files,ipmi = ipmi,interface_ip_prefix = interface_ip_prefix,scp=scp,
|
|
1366
|
+
files = files,ipmi = ipmi,interface_ip_prefix = interface_ip_prefix,scp=scp,gather_mode = gather_mode,
|
|
1283
1367
|
username=username,extraargs=extraargs,skipUnreachable=skipUnreachable,no_env=no_env,
|
|
1284
1368
|
greppable=greppable,skip_hosts = skip_hosts, file_sync = file_sync,error_only = error_only, shortend = shortend)
|
|
1285
1369
|
commandStr = '"' + '" "'.join(commands) + '"' if commands else ''
|
|
@@ -1288,7 +1372,7 @@ def getStrCommand(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAULT_ONE_O
|
|
|
1288
1372
|
def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAULT_ONE_ON_ONE, timeout = DEFAULT_TIMEOUT,password = DEFAULT_PASSWORD,
|
|
1289
1373
|
nowatch = DEFAULT_NO_WATCH,json = DEFAULT_JSON_MODE,called = _DEFAULT_CALLED,max_connections=DEFAULT_MAX_CONNECTIONS,
|
|
1290
1374
|
files = None,ipmi = DEFAULT_IPMI,interface_ip_prefix = DEFAULT_INTERFACE_IP_PREFIX,returnUnfinished = _DEFAULT_RETURN_UNFINISHED,
|
|
1291
|
-
scp=DEFAULT_SCP,username=DEFAULT_USERNAME,extraargs=DEFAULT_EXTRA_ARGS,skipUnreachable=DEFAULT_SKIP_UNREACHABLE,
|
|
1375
|
+
scp=DEFAULT_SCP,gather_mode = False,username=DEFAULT_USERNAME,extraargs=DEFAULT_EXTRA_ARGS,skipUnreachable=DEFAULT_SKIP_UNREACHABLE,
|
|
1292
1376
|
no_env=DEFAULT_NO_ENV,greppable=DEFAULT_GREPPABLE_MODE,willUpdateUnreachableHosts=_DEFAULT_UPDATE_UNREACHABLE_HOSTS,no_start=_DEFAULT_NO_START,
|
|
1293
1377
|
skip_hosts = DEFAULT_SKIP_HOSTS, curses_min_char_len = DEFAULT_CURSES_MINIMUM_CHAR_LEN, curses_min_line_len = DEFAULT_CURSES_MINIMUM_LINE_LEN,
|
|
1294
1378
|
single_window = DEFAULT_SINGLE_WINDOW,file_sync = False,error_only = DEFAULT_ERROR_ONLY):
|
|
@@ -1418,9 +1502,9 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
1418
1502
|
continue
|
|
1419
1503
|
if host.strip() in skipHostsList: continue
|
|
1420
1504
|
if file_sync:
|
|
1421
|
-
hosts.append(Host(host.strip(), os.path.dirname(command)+os.path.sep, files = [command],ipmi=ipmi,interface_ip_prefix=interface_ip_prefix,scp=scp,extraargs=extraargs))
|
|
1505
|
+
hosts.append(Host(host.strip(), os.path.dirname(command)+os.path.sep, files = [command],ipmi=ipmi,interface_ip_prefix=interface_ip_prefix,scp=scp,extraargs=extraargs,gatherMode=gather_mode))
|
|
1422
1506
|
else:
|
|
1423
|
-
hosts.append(Host(host.strip(), command, files = files,ipmi=ipmi,interface_ip_prefix=interface_ip_prefix,scp=scp,extraargs=extraargs))
|
|
1507
|
+
hosts.append(Host(host.strip(), command, files = files,ipmi=ipmi,interface_ip_prefix=interface_ip_prefix,scp=scp,extraargs=extraargs,gatherMode=gather_mode))
|
|
1424
1508
|
if not __global_suppress_printout:
|
|
1425
1509
|
print(f"Running command: {command} on host: {host}")
|
|
1426
1510
|
if not __global_suppress_printout: print('-'*80)
|
|
@@ -1462,9 +1546,9 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
1462
1546
|
continue
|
|
1463
1547
|
if host.strip() in skipHostsList: continue
|
|
1464
1548
|
if file_sync:
|
|
1465
|
-
hosts.append(Host(host.strip(), os.path.dirname(command)+os.path.sep, files = [command],ipmi=ipmi,interface_ip_prefix=interface_ip_prefix,scp=scp,extraargs=extraargs))
|
|
1549
|
+
hosts.append(Host(host.strip(), os.path.dirname(command)+os.path.sep, files = [command],ipmi=ipmi,interface_ip_prefix=interface_ip_prefix,scp=scp,extraargs=extraargs,gatherMode=gather_mode))
|
|
1466
1550
|
else:
|
|
1467
|
-
hosts.append(Host(host.strip(), command, files = files,ipmi=ipmi,interface_ip_prefix=interface_ip_prefix,scp=scp,extraargs=extraargs))
|
|
1551
|
+
hosts.append(Host(host.strip(), command, files = files,ipmi=ipmi,interface_ip_prefix=interface_ip_prefix,scp=scp,extraargs=extraargs,gatherMode=gather_mode))
|
|
1468
1552
|
if not __global_suppress_printout and len(commands) > 1:
|
|
1469
1553
|
print('-'*80)
|
|
1470
1554
|
print(f"Running command: {command} on hosts: {hostStr}" + (f"; skipping: {skipHostStr}" if skipHostStr else ''))
|
|
@@ -1532,7 +1616,7 @@ def main():
|
|
|
1532
1616
|
global __mainReturnCode
|
|
1533
1617
|
global __failedHosts
|
|
1534
1618
|
global __ipmiiInterfaceIPPrefix
|
|
1535
|
-
global
|
|
1619
|
+
global _binPaths
|
|
1536
1620
|
global _env_file
|
|
1537
1621
|
_emo = False
|
|
1538
1622
|
# We handle the signal
|
|
@@ -1548,6 +1632,7 @@ def main():
|
|
|
1548
1632
|
parser.add_argument("-f","--file", action='append', help="The file to be copied to the hosts. Use -f multiple times to copy multiple files")
|
|
1549
1633
|
parser.add_argument('--file_sync', action='store_true', help=f'Operate in file sync mode, sync path in <COMMANDS> from this machine to <HOSTS>. Treat --file <FILE> and <COMMANDS> both as source as source and destination will be the same in this mode. (default: {DEFAULT_FILE_SYNC})', default=DEFAULT_FILE_SYNC)
|
|
1550
1634
|
parser.add_argument('--scp', action='store_true', help=f'Use scp for copying files instead of rsync. Need to use this on windows. (default: {DEFAULT_SCP})', default=DEFAULT_SCP)
|
|
1635
|
+
parser.add_argument('-gm','--gather_mode', action='store_true', help=f'Gather files from the hosts instead of sending files to the hosts. Will send remote files specified in <FILE> to local path specified in <COMMANDS> (default: False)', default=False)
|
|
1551
1636
|
#parser.add_argument("-d",'-c',"--destination", type=str, help="The destination of the files. Same as specify with commands. Added for compatibility. Use #HOST# or #HOSTNAME# to replace the host name in the destination")
|
|
1552
1637
|
parser.add_argument("-t","--timeout", type=int, help=f"Timeout for each command in seconds (default: {DEFAULT_CLI_TIMEOUT} (disabled))", default=DEFAULT_CLI_TIMEOUT)
|
|
1553
1638
|
parser.add_argument("-r","--repeat", type=int, help=f"Repeat the command for a number of times (default: {DEFAULT_REPEAT})", default=DEFAULT_REPEAT)
|
|
@@ -1570,7 +1655,7 @@ def main():
|
|
|
1570
1655
|
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)
|
|
1571
1656
|
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)
|
|
1572
1657
|
parser.add_argument('--generate_default_config_file', action='store_true', help=f'Generate / store the default config file from command line argument and current config at {CONFIG_FILE}')
|
|
1573
|
-
parser.add_argument("-V","--version", action='version', version=f'%(prog)s {version}
|
|
1658
|
+
parser.add_argument("-V","--version", action='version', version=f'%(prog)s {version} with [ {", ".join(_binPaths.keys())} ] by {AUTHOR} ({AUTHOR_EMAIL})')
|
|
1574
1659
|
|
|
1575
1660
|
# parser.add_argument('-u', '--user', metavar='user', type=str, nargs=1,
|
|
1576
1661
|
# help='the user to use to connect to the hosts')
|
|
@@ -1623,7 +1708,7 @@ def main():
|
|
|
1623
1708
|
if not __global_suppress_printout:
|
|
1624
1709
|
print('> ' + getStrCommand(args.hosts,args.commands,oneonone=args.oneonone,timeout=args.timeout,password=args.password,
|
|
1625
1710
|
nowatch=args.nowatch,json=args.json,called=args.no_output,max_connections=args.max_connections,
|
|
1626
|
-
files=args.file,file_sync=args.file_sync,ipmi=args.ipmi,interface_ip_prefix=args.interface_ip_prefix,scp=args.scp,username=args.username,
|
|
1711
|
+
files=args.file,file_sync=args.file_sync,ipmi=args.ipmi,interface_ip_prefix=args.interface_ip_prefix,scp=args.scp,gather_mode = args.gather_mode,username=args.username,
|
|
1627
1712
|
extraargs=args.extraargs,skipUnreachable=args.skip_unreachable,no_env=args.no_env,greppable=args.greppable,skip_hosts = args.skip_hosts,
|
|
1628
1713
|
curses_min_char_len = args.window_width, curses_min_line_len = args.window_height,single_window=args.single_window,error_only=args.error_only))
|
|
1629
1714
|
if args.error_only:
|
|
@@ -1638,7 +1723,7 @@ def main():
|
|
|
1638
1723
|
hosts = run_command_on_hosts(args.hosts,args.commands,
|
|
1639
1724
|
oneonone=args.oneonone,timeout=args.timeout,password=args.password,
|
|
1640
1725
|
nowatch=args.nowatch,json=args.json,called=args.no_output,max_connections=args.max_connections,
|
|
1641
|
-
files=args.file,file_sync=args.file_sync,ipmi=args.ipmi,interface_ip_prefix=args.interface_ip_prefix,scp=args.scp,username=args.username,
|
|
1726
|
+
files=args.file,file_sync=args.file_sync,ipmi=args.ipmi,interface_ip_prefix=args.interface_ip_prefix,scp=args.scp,gather_mode = args.gather_mode,username=args.username,
|
|
1642
1727
|
extraargs=args.extraargs,skipUnreachable=args.skip_unreachable,no_env=args.no_env,greppable=args.greppable,skip_hosts = args.skip_hosts,
|
|
1643
1728
|
curses_min_char_len = args.window_width, curses_min_line_len = args.window_height,single_window=args.single_window,error_only=args.error_only)
|
|
1644
1729
|
#print('*'*80)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|