package cli import ( "fmt" "strings" "github.com/viert/xc/term" ) type helpItem struct { help string usage string isTopic bool } var ( execHelp = &helpItem{ usage: " ", help: `Runs a command on a list of servers. List of hosts is represented by in its own syntax which can be learned by using "help expressions" command. exec can proceed in 3 different modes: serial, parallel and collapse. In ` + term.Colored("serial", term.CWhite, true) + ` mode the command will be called server by server sequentally. Between servers in list xc will hold for a delay which can be set with command "delay". In ` + term.Colored("parallel", term.CWhite, true) + ` mode the command will be executed simultaneously. All output will be prefixed by host name which the output line belongs to. Output is (almost) non buffered so one can use parallel mode to run "infinite" commands like "tail -f" which is handy for watching logs from the whole cluster in real-time. The ` + term.Colored("collapse", term.CWhite, true) + ` mode is a lot like the parallel mode however the whole output is hidden until the execution is over. In this mode xc prints the result grouped by the output so the differences between hosts become more obvious. Try running "exec %group cat /etc/redhat-release" on a big group of hosts in collapse mode to see if they have the same version of OS for example. While the execution mode can be switched by "mode" command, there's a couple of shortcuts: c_exec p_exec s_exec which are capable to run exec in collapse, parallel or serial mode correspondingly without switching the execution mode`, } runScriptHelp = &helpItem{ usage: " ", help: `Runs a local script on a given list of hosts. To learn mode about type "help expressions". runscript simply copies the script to every server in the list and then run it according to current execution mode (Type "help exec" to learn more on execution modes), i.e. it can run in parallel or sequentally like exec does. There are also shortcut aliases c_runscript, s_runscript and p_runscript for calling runscript in a particular execution mode without permanent switching to it.`, } modeHelp = `Switches execution mode To learn more about execution modes type "help exec". Xc has shortcuts for switching modes: just type "parallel", "serial" or "collapse" and it will switch the mode correspondingly.` helpStrings = map[string]*helpItem{ "alias": &helpItem{ usage: " []", help: `Creates a local alias. This is handy for longer commands which are often in use. Example: alias ls local ls - this will create a local alias "ls" which actually runs "local ls" alias uptime p_exec #1 uptime - this creates a local alias "uptime" which runs "p_exec uptime" will be taken from the alias command and put into p_exec command, i.e. uptime %mygroup will run p_exec %mygroup uptime Every alias created disappears after xc exits. To make an alias persistent put it into rcfile. See "help rcfiles" for further info.`, }, "cd": &helpItem{ usage: "", help: "Changes working directory", }, "config": &helpItem{ isTopic: true, help: `Configuration file is located in ~/.xc.conf. The first time xc starts it creates a default configuration file with all the settings set to default values: [main] user = mode = parallel history_file = ~/.xc_history cache_dir = ~/.xc_cache cache_ttl = 336 # 24 * 7 * 2 rc_file = ~/.xcrc raise = none exit_confirm = true exec_confirm = false log_file = ~/xc.log distribute = scp debug = false [executer] ssh_threads = 50 ssh_connect_timeout = 1 ssh_command = /usr/bin/ssh progress_bar = true prepend_hostnames = true remote_tmpdir = /tmp delay = 0 interpreter = bash interpreter_sudo = sudo bash interpreter_su = su - [backend] type = inventoree url = http://inventory-stage.infra.cloud.devmail.ru auth_token = work_groups = WorkGroup1,WorkGroup2 host_key_field = ssh_hostname Configuration is split to 3 sections: main, executer and backend. [main] user is the user which will be set on xc startup. If empty, the current system user is used. mode is the execution mode which will be set on xc startup. See "help mode" for more info on execution modes. history_file sets the history file cache_dir sets the cache dir for data derived from inventoree cache_ttl sets cache ttl (in hours) rc_file is the rcfile which will be executed on xc startup. See "help rcfiles" for more info. raise is the raise mode which will be set on xc startup exit_confirm is boolean setting for disable or enable confirmation on exit distribute sets initial distribute type to either tar or scp. See "help distribute_type" to learn more. debug sets initial debug logging on/off. [executer] ssh_threads limits the number of simultaneously running ssh commands. ssh_connect_timeout sets the default ssh connect timeout. You can change it at any moment using connect_timeout command. progress_bar sets progressbar on or off on xc startup remote_tmpdir is a temporary directory used on remote servers for various xc needs delay sets a delay in seconds between hosts when executing in serial mode. See "help delay" for more info interpreter_* sets commands executed remotely to boot the necessary interpreter according to current "raise" mode The [backend] section sets data storage backend. Three backends are currently supported: inventoree, conductor and ini. The backend type is set by a mandatory option "type". 1. "ini" backend stores hosts and groups in a local ini-file. There's only one option "filename" to tell xc where to find the ini-file. Example of ini-file: [workgroups] workgroup1 [groups] group1 work_group=workgroup1 group2 work_grlup=workgroup2 parent=group1 [hosts] host1.example.com group=group1 datacenter=dc1.1 host2.example.com group=group2 datacenter=dc1.1 [datacenters] dc1 dc1.1 parent=dc1 2. "conductor" loads hosts and groups via inventoree v1 API which is deprecated 3. "inventoree" is the most modern way to store your data. Options are following: url - a base url to inventoree instance (inventoree >= 7.0 is required) auth_token - your personal auth token work_groups - a comma-separated list of workgroups to load. If the list is empty, xc will load all the workgroups which could increase loading time dramatically. host_key_field - may be set to either "fqdn" or "ssh_hostname", this tells xc what a host is identified by. ssh_hostname in its turn is a computed field in inventoree >= 7.2-45 which may be configured in custom data field "ssh_hostname" like aliases are configured (using $0, $1, $2 etc as domain parts) `, }, "rcfiles": &helpItem{ isTopic: true, help: `Rcfile configured in .xc.conf file is executed every time xc starts. It may be useful for configuring aliases (as they are dropped when xc exits) and other options. Rcfile is just a number of xc commands in a text file.`, }, "passmgr": &helpItem{ isTopic: true, help: `Password manager is a golang plugin which must have two exported functions: func Init(options map[string]string, debugf func(string, ...interface{})) error func GetPass(host string) string Init function is called on xc start passing all the options found in [passmgr] config section as a map[string]string, and a debugf function which logs to xc shared log if it's enabled. GetPass function is called to acquire proper password for a host. Xc passes a hostname as the only argument and expects function to return a password. For more info on how to write golang plugins, please refer to golang documentation or this article: https://medium.com/learning-the-go-programming-language/writing-modular-go-programs-with-plugins-ec46381ee1a9`, }, "debug": &helpItem{ usage: "", help: `An internal debug. May cause unexpected output. One shouldn't use it unless she knows what she's doing.`, }, "delay": &helpItem{ usage: "", help: `Sets a delay between hosts when in serial mode. This is useful for soft restarting i.e. when you want to give a service some time to warm up before restarting it on next host.`, }, "distribute": &helpItem{ usage: " ", help: `Distributes a local file to a number of hosts listed in "host_expression" in parallel. See "help expressions" for further info on . Example: distribute %mygroup hello.txt`, }, "distribute_type": &helpItem{ usage: "", help: `Sets the backend used for the "distribute" command. "tar" option should work faster, it also supports symlinks but may spend more memory. "scp" is slower and may let you down in copying directories with symlinks. It's more stable though.`, }, "expressions": &helpItem{ help: `A lot of commands in xc use host expressions with a certain syntax to represent a list of hosts. Every expression is a comma-separated list of tokens, where token may be - a single host, - a single group, - a single workgroup, and every item may optionally be limited to a particular datacenter, a given tag, or even be completely excluded from the list. Some self-explanatory examples: host1,host2 - simple host list containing 2 hosts %group1 - a group of hosts taken from inventoree %group1,host1 - all hosts from group1, plus host1 %group1,-host2 - all hosts from group1, excluding(!) host2 %group2@dc1 - all hosts from group2, located in datacenter dc1 *myworkgroup@dc2,-%group3,host5 - all hosts from wg "myworkgroup" excluding hosts from group3, plus host5 %group5#tag1 - all hosts from group5 tagged with tag1 You may combine any number of tokens keeping in mind that they are resolved left to right, so exclusions almost always should be on the righthand side. For example, "-host1,host1" will end up with host1 in list despite being excluded previously.`, isTopic: true, }, "exec": execHelp, "s_exec": execHelp, "c_exec": execHelp, "p_exec": execHelp, "exit": &helpItem{ usage: "", help: "Exits the xc program. You can also use Ctrl-D to quit xc.", }, "help": &helpItem{ usage: "[]", help: "Shows help on various commands and topics", }, "hostlist": &helpItem{ usage: "", help: `Resolves the host expression and prints the resulting hostlist. To learn more about expressions use "help expressions" command`, }, "local": &helpItem{ usage: "", help: `Runs local command. For example you may want to ping a host without leaving the xc. This can be done by typing "local ping 1.1.1.1". For frequently used commands you may want to create aliases like so: alias ping local ping #*. This will create an alias "ping" so you won't have to type "local" in front of "ping" anymore. To learn more about aliases type "help alias"`, }, "mode": &helpItem{ usage: "", help: modeHelp, }, "parallel": &helpItem{ usage: "", help: modeHelp, }, "serial": &helpItem{ usage: "", help: modeHelp, }, "collapse": &helpItem{ usage: "", help: modeHelp, }, "prepend_hostnames": &helpItem{ usage: "", help: `Sets prepend hostnames mode on or off. When calling without arguments, shows the current value. This switches the appearence of hostnames before the output lines in parallel mode. Switching them off is useful for copy-pasting the results.`, }, "passwd": &helpItem{ usage: "", help: `Sets the password for raising privileges`, }, "progressbar": &helpItem{ usage: "[]", help: `Sets the progressbar on or off. If no value is given, prints the current value.`, }, "natural_sort": &helpItem{ usage: "[]", help: `Sets natural sorting of host lists on or off. If no value is given, prints the current value. Important note: sorting works only within one expression token, that is, if you explicitly type the hostlist like "host5,host3,host1", the order is preserved, xc considers you know what you are doing. Sorting is applied only within a group, a workgroup or a pattern like "host{1..50}"`, }, "raise": &helpItem{ usage: "", help: `Sets the type of raising privileges during running the "exec" command. If the value is "none", no attempts to raise privileges will be made.`, }, "reload": &helpItem{ usage: "", help: `Reloads hosts and groups data from inventoree and rewrites the cache`, }, "runscript": runScriptHelp, "c_runscript": runScriptHelp, "p_runscript": runScriptHelp, "s_runscript": runScriptHelp, "interpreter": &helpItem{ usage: "[raise_type interpreter]", help: `When invoking without arguments, the command shows the current interpreters for each type of privileges rasing ("help raise" to learn more on that). You can redefine interpreter using this command as in the given examples: interpreter su su -m interpreter sudo sudo /bin/bash interpreter none /bin/sh`, }, "output": &helpItem{ usage: "[filename]", help: `Copies the entire output of parallel(!) exec commands to a given logfile. To switch the logging off, type "output _". When invoked without arguments, output command prints the current output filename (or a message saying that the output logging is switched off) and exits.`, }, "ssh": &helpItem{ usage: "", help: `Starts ssh session to hosts one by one, raising the privileges if raise type is not "none" ("help raise" to learn more) and gives the control to user. When user exits the session xc moves on to the next server.`, }, "threads": &helpItem{ usage: "[num_threads]", help: `Sets max number of simultaneously running ssh threads to . When called without arguments, prints the current value.`, }, "use_password_manager": &helpItem{ usage: "[]", help: `Sets the password manager on/off. If no value is given, prints the current value. If password manager is not ready, setting this value to "on" will print an error.`, }, "user": &helpItem{ usage: "", help: `Sets the username for all the execution commands. This is used to get access to hosts via ssh/scp.`, }, } ) func (c *Cli) doHelp(name string, argsLine string, args ...string) { if len(args) < 1 { generalHelp() return } if hs, found := helpStrings[args[0]]; found { if hs.isTopic { fmt.Printf("\nTopic: %s\n\n", term.Colored(args[0], term.CWhite, true)) } else { fmt.Printf("\nCommand: %s %s\n\n", term.Colored(args[0], term.CWhite, true), hs.usage) } tokens := strings.Split(hs.help, "\n") for _, token := range tokens { fmt.Printf(" %s\n", token) } fmt.Println() } else { term.Errorf("There's no help on topic \"%s\"\n", args[0]) } } func generalHelp() { fmt.Println(` List of commands: alias creates a local alias command cd changes current working directory collapse shortcut for "mode collapse" debug one shouldn't use this delay sets a delay between hosts in serial mode distribute copies a file to a number of hosts in parallel distribute_type sets the backend of the "distribute" command exec/c_exec/s_exec/p_exec executes a remote command on a number of hosts exit exits the xc help shows help on various topics hostlist resolves a host expression to a list of hosts interpreter sets interpreter for each type of privileges raising local starts a local command mode switches between execution modes natural_sort sets natural sorting on/off parallel shortcut for "mode parallel" passwd sets passwd for privilege raise progressbar controls progressbar raise sets the privilege raise mode reload reloads hosts and groups data from inventoree runscript runs a local script on a number of remote hosts serial shortcut for "mode serial" ssh starts ssh session to a number of hosts sequentally use_password_manager turns password manager on/off user sets current user`) fmt.Println() }