mirror of
https://github.com/house-of-vanity/rexec.git
synced 2025-07-08 09:14:08 +00:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
f67342de1a | |||
98c48af331 | |||
3cf1e79883 | |||
0d95364e50 | |||
48635fc091 | |||
5ed62d9323 | |||
c635ed0dd0 | |||
ad2d36bb35 | |||
4f1657c37c | |||
a28d1a3a38 | |||
502d206dc7 | |||
e661349034 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
target
|
target
|
||||||
|
pkg
|
||||||
|
*zst
|
||||||
|
@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|
||||||
|
## [1.0.4] - 2023-08-14
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Expansion host strings.
|
||||||
|
|
||||||
## [1.0.1] - 2023-06-20
|
## [1.0.1] - 2023-06-20
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1043,7 +1043,7 @@ checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rexec"
|
name = "rexec"
|
||||||
version = "1.0.1"
|
version = "1.0.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brace-expand",
|
"brace-expand",
|
||||||
"clap 4.3.4",
|
"clap 4.3.4",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rexec"
|
name = "rexec"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
|
readme = "https://github.com/house-of-vanity/rexec#readme"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Parallel SSH executor"
|
description = "Parallel SSH executor"
|
||||||
repository = "https://github.com/house-of-vanity/rexec"
|
repository = "https://github.com/house-of-vanity/rexec"
|
||||||
|
25
PKGBUILD
Normal file
25
PKGBUILD
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Maintainer: AB <gh@hexor.ru>
|
||||||
|
pkgname=rexec
|
||||||
|
pkgver=1.0.4.r0.g3cf1e79
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="Parallel SSH executor"
|
||||||
|
url="https://github.com/house-of-vanity/rexec"
|
||||||
|
license=("WTFPL")
|
||||||
|
arch=("x86_64")
|
||||||
|
makedepends=("cargo")
|
||||||
|
|
||||||
|
pkgver() {
|
||||||
|
(git describe --long --tags || echo "$pkgver") | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd ..
|
||||||
|
usrdir="$pkgdir/usr"
|
||||||
|
mkdir -p $usrdir
|
||||||
|
cargo install --no-track --path . --root "$usrdir"
|
||||||
|
}
|
||||||
|
|
109
src/main.rs
109
src/main.rs
@ -1,4 +1,13 @@
|
|||||||
use brace_expand::brace_expand;
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use dialoguer::Confirm;
|
use dialoguer::Confirm;
|
||||||
use dns_lookup::lookup_host;
|
use dns_lookup::lookup_host;
|
||||||
@ -7,17 +16,6 @@ use itertools::Itertools;
|
|||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use massh::{MasshClient, MasshConfig, MasshHostConfig, SshAuth};
|
use massh::{MasshClient, MasshConfig, MasshHostConfig, SshAuth};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs::read_to_string;
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::net::IpAddr;
|
|
||||||
|
|
||||||
use std::process;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
|
|
||||||
// Define args
|
// Define args
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@ -26,10 +24,18 @@ struct Args {
|
|||||||
#[arg(short, long, default_value_t = whoami::username())]
|
#[arg(short, long, default_value_t = whoami::username())]
|
||||||
username: String,
|
username: String,
|
||||||
|
|
||||||
#[arg(short, long, help = "Use known_hosts to build servers list")]
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
help = "Use known_hosts to build servers list instead of string expansion."
|
||||||
|
)]
|
||||||
known_hosts: bool,
|
known_hosts: bool,
|
||||||
|
|
||||||
#[arg(short, long, help = "Expression to build server list")]
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
help = "Expression to build server list. List and range expansion are supported. Example: 'web-[1:12]-io-{prod,dev}'"
|
||||||
|
)]
|
||||||
expression: String,
|
expression: String,
|
||||||
|
|
||||||
#[arg(short, long, help = "Command to execute on servers")]
|
#[arg(short, long, help = "Command to execute on servers")]
|
||||||
@ -75,16 +81,69 @@ fn read_known_hosts() -> Vec<Host> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_expression(expr: &str) -> Vec<Host> {
|
fn expand_range(start: i32, end: i32) -> Vec<String> {
|
||||||
let mut result: Vec<Host> = Vec::new();
|
(start..=end).map(|i| i.to_string()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
for hostname in brace_expand(expr) {
|
fn expand_list(list: &str) -> Vec<String> {
|
||||||
result.push(Host {
|
list.split(',').map(|s| s.to_string()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_string(s: &str) -> Vec<Host> {
|
||||||
|
let mut hosts: Vec<Host> = Vec::new();
|
||||||
|
let mut result = vec![s.to_string()];
|
||||||
|
|
||||||
|
while let Some(r) = result.iter().find(|s| s.contains('[')) {
|
||||||
|
let r = r.clone();
|
||||||
|
let start = r.find('[').unwrap();
|
||||||
|
let end = match r[start..].find(']') {
|
||||||
|
None => {
|
||||||
|
error!("Error parsing host expression. Wrong range expansion '[a:b]'");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
Some(s) => s + start,
|
||||||
|
};
|
||||||
|
let colon = match r[start..end].find(':') {
|
||||||
|
None => {
|
||||||
|
error!("Error parsing host expression. Missing colon in range expansion '[a:b]'");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
Some(c) => c + start,
|
||||||
|
};
|
||||||
|
let low = r[start + 1..colon].parse::<i32>().unwrap();
|
||||||
|
let high = r[colon + 1..end].parse::<i32>().unwrap();
|
||||||
|
result.retain(|s| s != &r);
|
||||||
|
for val in expand_range(low, high) {
|
||||||
|
let new_str = format!("{}{}{}", &r[..start], val, &r[end + 1..]);
|
||||||
|
result.push(new_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(r) = result.iter().find(|s| s.contains('{')) {
|
||||||
|
let r = r.clone();
|
||||||
|
let start = r.find('{').unwrap();
|
||||||
|
let end = match r.find('}') {
|
||||||
|
None => {
|
||||||
|
error!("Error parsing host expression. Wrong range expansion '{{one,two}}'");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
Some(s) => s,
|
||||||
|
};
|
||||||
|
let list = &r[start + 1..end];
|
||||||
|
result.retain(|s| s != &r);
|
||||||
|
for val in expand_list(list) {
|
||||||
|
let new_str = format!("{}{}{}", &r[..start], val, &r[end + 1..]);
|
||||||
|
result.push(new_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for hostname in result {
|
||||||
|
hosts.push(Host {
|
||||||
name: hostname.to_string(),
|
name: hostname.to_string(),
|
||||||
ip: None,
|
ip: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
result
|
hosts
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -112,7 +171,7 @@ fn main() {
|
|||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
info!("Using string expansion to build server list.");
|
info!("Using string expansion to build server list.");
|
||||||
expand_expression(&args.expression)
|
expand_string(&args.expression)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dedup hosts from known_hosts file
|
// Dedup hosts from known_hosts file
|
||||||
@ -121,6 +180,16 @@ fn main() {
|
|||||||
// Build MasshHostConfig hostnames list
|
// Build MasshHostConfig hostnames list
|
||||||
let mut massh_hosts: Vec<MasshHostConfig> = vec![];
|
let mut massh_hosts: Vec<MasshHostConfig> = vec![];
|
||||||
let mut hosts_and_ips: HashMap<IpAddr, String> = HashMap::new();
|
let mut hosts_and_ips: HashMap<IpAddr, String> = HashMap::new();
|
||||||
|
if args.parallel != 100 {
|
||||||
|
warn!("Parallelism: {} thread{}", &args.parallel, {
|
||||||
|
if args.parallel != 1 {
|
||||||
|
"s."
|
||||||
|
} else {
|
||||||
|
"."
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
info!("Matched hosts:");
|
info!("Matched hosts:");
|
||||||
for host in matched_hosts.iter() {
|
for host in matched_hosts.iter() {
|
||||||
let ip = match lookup_host(&host.name) {
|
let ip = match lookup_host(&host.name) {
|
||||||
|
Reference in New Issue
Block a user