From 157cda66b7dc2f78d078caff1c84ccd81f980103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D1=80=D0=BE=D0=B1=D1=8C=D0=B5=D0=B2=20=D0=9F?= =?UTF-8?q?=D0=B0=D0=B2=D0=B5=D0=BB?= Date: Mon, 3 Aug 2020 18:02:19 +0300 Subject: [PATCH] hosts files --- cli/completer.go | 5 +++++ cli/handlers.go | 12 ++++++++++++ cli/help.go | 8 +++++--- cmd/xc/main.go | 6 ++++++ store/parser.go | 21 +++++++++++++++++++++ store/store.go | 18 ++++++++++++++++++ 6 files changed, 67 insertions(+), 3 deletions(-) diff --git a/cli/completer.go b/cli/completer.go index 85dfb65..4248957 100644 --- a/cli/completer.go +++ b/cli/completer.go @@ -176,6 +176,11 @@ func (x *completer) completeExec(line []rune) ([][]rune, int) { return x.completeTag(line[1:]) } + if len(line) > 0 && line[0] == '&' { + comp, ll := completeFiles(line[1:]) + return comp, ll + 1 + } + return x.completeHost(line) } diff --git a/cli/handlers.go b/cli/handlers.go index c7ef912..dac5594 100644 --- a/cli/handlers.go +++ b/cli/handlers.go @@ -6,6 +6,7 @@ import ( "os/exec" "os/signal" "runtime" + "runtime/debug" "strconv" "syscall" @@ -56,6 +57,7 @@ func (c *Cli) setupCmdHandlers() { c.handlers["version"] = c.doVersion c.handlers["goruntime"] = c.doGoruntime c.handlers["natural_sort"] = c.doNaturalSort + c.handlers["_mem"] = c.doMemoryDump commands := make([]string, len(c.handlers)) i := 0 @@ -74,6 +76,16 @@ func (c *Cli) doExit(name string, argsLine string, args ...string) { c.stopped = true } +func (c *Cli) doMemoryDump(name string, argsLine string, args ...string) { + f, err := os.Create("/tmp/xcheap.dump") + if err != nil { + term.Errorf("Can't dump: %s\n", err) + return + } + defer f.Close() + debug.WriteHeapDump(f.Fd()) +} + func (c *Cli) doMode(name string, argsLine string, args ...string) { if len(args) < 1 { term.Errorf("Usage: mode <[serial,parallel,collapse]>\n") diff --git a/cli/help.go b/cli/help.go index 40781e1..4198387 100644 --- a/cli/help.go +++ b/cli/help.go @@ -252,7 +252,8 @@ with symlinks. It's more stable though.`, Every expression is a comma-separated list of tokens, where token may be - a single host, - a single group, - - a single workgroup, + - a single workgroup, + - a filename containing a list of hosts and every item may optionally be limited to a particular datacenter, a given tag, or even be completely excluded from the list. @@ -263,8 +264,9 @@ Some self-explanatory examples: %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 - + %group5#tag1 - all hosts from group5 tagged with tag1 + &hosts.txt - hosts from file hosts.txt + 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.`, diff --git a/cmd/xc/main.go b/cmd/xc/main.go index 6db9036..e6bb7c7 100644 --- a/cmd/xc/main.go +++ b/cmd/xc/main.go @@ -1,6 +1,7 @@ package main import ( + "net/http" "os" "path" "strings" @@ -9,12 +10,17 @@ import ( "github.com/viert/xc/backend/inventoree" "github.com/viert/xc/backend/localini" + _ "net/http/pprof" + "github.com/viert/xc/cli" "github.com/viert/xc/config" "github.com/viert/xc/term" ) func main() { + + go http.ListenAndServe(":5001", nil) + var tool *cli.Cli var err error diff --git a/store/parser.go b/store/parser.go index 3a957df..65c8f0a 100644 --- a/store/parser.go +++ b/store/parser.go @@ -14,6 +14,7 @@ const ( tTypeGroup tTypeWorkGroup tTypeHostRegexp + tTypeHostListFile ) const ( @@ -25,6 +26,7 @@ const ( stateReadTag stateReadHostBracePattern stateReadRegexp + stateReadHostListFile ) type token struct { @@ -83,6 +85,12 @@ func parseExpression(expr []rune) ([]*token, error) { continue } + if sym == '&' { + ct.Type = tTypeHostListFile + state = stateReadHostListFile + continue + } + if sym == '/' || sym == '~' { state = stateReadHost ct.Type = tTypeHostRegexp @@ -214,6 +222,19 @@ func parseExpression(expr []rune) ([]*token, error) { } ct.Value += string(sym) + + case stateReadHostListFile: + if sym == ',' || last { + if last && sym != ',' { + ct.Value += string(sym) + } + res = append(res, ct) + ct = newToken() + state = stateWait + continue + } + ct.Value += string(sym) + case stateReadHostBracePattern: if sym == '{' { return nil, fmt.Errorf("nested patterns are not allowed (at %d)", i) diff --git a/store/store.go b/store/store.go index 1026bbf..f67046e 100644 --- a/store/store.go +++ b/store/store.go @@ -1,6 +1,8 @@ package store import ( + "bufio" + "os" "regexp" "sort" "strings" @@ -179,6 +181,22 @@ func (s *Store) HostList(expr []rune) ([]string, error) { } switch token.Type { + case tTypeHostListFile: + filename := token.Value + f, err := os.Open(filename) + if err != nil { + return nil, err + } + sc := bufio.NewScanner(f) + for sc.Scan() { + hostname := strings.Trim(sc.Text(), " \t\r\n") + if hostname == "" || strings.Contains(hostname, " ") || strings.HasPrefix(hostname, "#") { + continue + } + etoken.hosts = append(etoken.hosts, hostname) + } + f.Close() + case tTypeHostRegexp: for _, host := range s.matchHost(token.RegexpFilter) { etoken.hosts = append(etoken.hosts, host)