Version 0.7.0 Implement command-line
- Now allows for command-line execution against hosts
This commit is contained in:
13
TODO.txt
13
TODO.txt
@ -1,5 +1,9 @@
|
||||
TODO
|
||||
- fix handling of control-c
|
||||
- We should throw out fabric; paramiko does everything we need - we can open a shell and send signals to handle control-C
|
||||
and other sorts of situation handling. We'll basically have to rewrite how fabtools works but it should be relatively
|
||||
trivial since we already basically handle everything at the paramiko level. We may have to parse sshconfig ourselves
|
||||
but it shouldn't be too big a deal.
|
||||
- fix handling of control-c (sorta works sometimes)
|
||||
- redo the command loop to make some fuckin sense why do we use eceptions to do modal stuff
|
||||
- make styles optional
|
||||
- expose styles in config file or personal config
|
||||
@ -11,7 +15,6 @@ TODO
|
||||
- add commands to show the list of captured outputs, along with the commands and targets
|
||||
- add commands to search captured outputs
|
||||
- add commands to search captured outputs and put them into a variable that can be used for /target etc.
|
||||
- calling commands and aliases from command line (e.g. multiball -t <targets> -c <command>)
|
||||
- tagging and filtering commands (so `noupgrade` gets a tag, and we can filter them)
|
||||
- saving the last host set in a .file
|
||||
- allow server groups / server labels
|
||||
@ -26,5 +29,9 @@ TODO
|
||||
- implement interactive alias system
|
||||
- Catch more exceptions in fabtools, and also add retries
|
||||
- Make the runner aware of multiple commands so that it can combine outputs and make 'overall success' or 'overall failure'
|
||||
- make C-c break the connections not the program
|
||||
- make C-c break the connections not the program (this requires throwing out fabric)
|
||||
- make a /summary command that *only* outputs the summary of a remote command, not the stdout/stderr
|
||||
- document more things about multiball
|
||||
- allow a user-specific configuration that isn't in the current directory
|
||||
- make a set of commands that modulate the /all list, so /all goes back to the real default not the total default
|
||||
- the ability to deploy files
|
||||
|
@ -1 +1 @@
|
||||
__version__ = "0.6.0"
|
||||
__version__ = "0.7.0"
|
||||
|
@ -31,6 +31,7 @@ from .fabtools import HostSet
|
||||
from .tools import (load_bare_server_list, load_config,
|
||||
load_sshconfig_server_list)
|
||||
|
||||
oprint = print
|
||||
from .style import print, STYLES
|
||||
|
||||
class MultiballCommandException(BaseException):
|
||||
@ -116,13 +117,13 @@ class Multiball:
|
||||
self.ssh_config = Path(sc).expanduser()
|
||||
newhosts = load_sshconfig_server_list(
|
||||
Path(sc).expanduser())
|
||||
print(f"<message>Loaded {len(newhosts)} from {sc}</message>")
|
||||
print(f"<message>Loaded {len(newhosts)} host(s) from {sc}</message>")
|
||||
self.allhosts.update(newhosts)
|
||||
|
||||
if bareconfigs := config.get('hostlists'):
|
||||
for sc in bareconfigs.split(','):
|
||||
newhosts = load_bare_server_list(Path(sc).expanduser())
|
||||
print(f"<message>Loaded {len(newhosts)} from {sc}</message>")
|
||||
print(f"<message>Loaded {len(newhosts)} host(s) from {sc}</message>")
|
||||
self.allhosts.update(newhosts)
|
||||
|
||||
else:
|
||||
@ -417,12 +418,59 @@ class Multiball:
|
||||
# Nothing of importance happens here.
|
||||
...
|
||||
|
||||
def parse_args(argv):
|
||||
if (len(argv) > 1):
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='multiball',
|
||||
description='Run commands on a number of hosts.',
|
||||
epilog='With no arguments enter interactive mode.',
|
||||
)
|
||||
parser.add_argument('--confirm', action='store_true', help='Skip confirmation step, use with caution.')
|
||||
parser.add_argument('-t', '--target', type=str, nargs='?', action='append', help='Select one or more targets.')
|
||||
parser.add_argument('-a', '--all', action='store_true', help='Select default target list.')
|
||||
parser.add_argument('-c', '--command', type=str, nargs='+', action='append', help='Command to run.')
|
||||
res = parser.parse_args(argv)
|
||||
return res
|
||||
else:
|
||||
return None
|
||||
|
||||
def multiball(*args, **kwargs):
|
||||
|
||||
def multiball(argv):
|
||||
args = parse_args(argv)
|
||||
|
||||
## Command mode
|
||||
if (args):
|
||||
if (not args.all and (len(args.target) <= 0)):
|
||||
print('Need either --all or at least one --target')
|
||||
return 1
|
||||
if (len(args.command) <= 0):
|
||||
print('Need at least one --command')
|
||||
return 1
|
||||
|
||||
print(f"<alert>Multiball {__version__}.</alert>")
|
||||
mb = Multiball()
|
||||
|
||||
if (args.all):
|
||||
mb.targethosts = mb.allhosts
|
||||
else:
|
||||
mb.targethosts = args.target
|
||||
|
||||
for command in args.command:
|
||||
mb._print_targetlist()
|
||||
command = ' '.join(command)
|
||||
if (not args.confirm):
|
||||
try:
|
||||
mb.command_confirm('', args=f"Run `{command}`?")
|
||||
except MBCommandAbort:
|
||||
continue
|
||||
mb._run_remote_command(command)
|
||||
return 0
|
||||
|
||||
## Interactive mode
|
||||
print(f"<alert>Welcome to Multiball {__version__}. type /help for help.</alert>")
|
||||
mb = Multiball()
|
||||
# loop on prompt
|
||||
while (True):
|
||||
# loop on prompt
|
||||
try:
|
||||
mb.cmd_prompt()
|
||||
except MBExit:
|
||||
@ -431,7 +479,7 @@ def multiball(*args, **kwargs):
|
||||
|
||||
def main():
|
||||
try:
|
||||
sys.exit(multiball(sys.argv))
|
||||
sys.exit(multiball(sys.argv[1:]))
|
||||
except EOFError:
|
||||
sys.exit(0)
|
||||
except Exception as inst:
|
||||
|
@ -19,6 +19,7 @@ STYLES = Style.from_dict({
|
||||
|
||||
|
||||
def print(message):
|
||||
message = message.replace("&", "&")
|
||||
print_formatted_text(HTML(f"<default>{message}</default>"), style=STYLES)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user