stubbing pdsh
This commit is contained in:
parent
e797a14911
commit
5a62622892
6
go.mod
6
go.mod
@ -6,11 +6,11 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/djherbis/times v1.6.0
|
github.com/djherbis/times v1.6.0
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/shirou/gopsutil/v4 v4.25.6
|
github.com/shirou/gopsutil/v4 v4.25.7
|
||||||
golang.org/x/sync v0.16.0
|
golang.org/x/sync v0.16.0
|
||||||
golang.org/x/sys v0.34.0
|
golang.org/x/sys v0.35.0
|
||||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8
|
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8
|
||||||
r00t2.io/goutils v1.9.2
|
r00t2.io/goutils v1.9.4
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
8
go.sum
8
go.sum
@ -19,6 +19,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
|||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||||
@ -35,9 +37,15 @@ golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 h1:FW42yWB1sGClqswyHIB68wo0+oPrav1IuQ+Tdy8Qp8E=
|
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 h1:FW42yWB1sGClqswyHIB68wo0+oPrav1IuQ+Tdy8Qp8E=
|
||||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE=
|
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE=
|
||||||
r00t2.io/goutils v1.9.2 h1:1rcDgJ3MorWVBmZSvLpbAUNC+J+ctRfJQq5Wliucjww=
|
r00t2.io/goutils v1.9.2 h1:1rcDgJ3MorWVBmZSvLpbAUNC+J+ctRfJQq5Wliucjww=
|
||||||
r00t2.io/goutils v1.9.2/go.mod h1:76AxpXUeL10uFklxRB11kQsrtj2AKiNm8AwG1bNoBCA=
|
r00t2.io/goutils v1.9.2/go.mod h1:76AxpXUeL10uFklxRB11kQsrtj2AKiNm8AwG1bNoBCA=
|
||||||
|
r00t2.io/goutils v1.9.3 h1:pR9Ggu5JBpVjfrqNBrZg9bZpKan0TCcwt3MXrSdkhLo=
|
||||||
|
r00t2.io/goutils v1.9.3/go.mod h1:76AxpXUeL10uFklxRB11kQsrtj2AKiNm8AwG1bNoBCA=
|
||||||
|
r00t2.io/goutils v1.9.4 h1:+Bm72mKhgXs6DRtU3P4sBjqUNwAKAFfdF9lx5bomwQY=
|
||||||
|
r00t2.io/goutils v1.9.4/go.mod h1:76AxpXUeL10uFklxRB11kQsrtj2AKiNm8AwG1bNoBCA=
|
||||||
|
23
pdsh/docs.go
23
pdsh/docs.go
@ -1,16 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
Package pdsh (!! WIP !!) provides PDSH-compatible functionality for parsing group files.
|
Package pdsh (!! WIP !!) provides PDSH-compatible functionality for parsing group/gender/etc. files/sources.
|
||||||
|
|
||||||
Note that this library will *only* source and parse PDSH-compatible host/group files,
|
Note that this library will *only* generate the host list/etc.,
|
||||||
it will not actually connect to anything.
|
it will not actually connect to anything.
|
||||||
It simply provides ways of returning lists of hosts using generation rules/patterns.
|
It simply provides ways of returning lists of hosts using generation rules/patterns.
|
||||||
|
Said another way, it does not implement any of PDSH's "rcmd" modules, only the "misc" modules.
|
||||||
|
|
||||||
Currently, the only supported PDSH module is `misc/dshgroup` but additional/all other
|
(As a hint, you can implement SSH connections via [golang.org/x/crypto/ssh] in goroutine'd functions
|
||||||
|
using this package to generate the target addresses, etc.)
|
||||||
|
|
||||||
|
Currently, the only supported PDSH module is misc/dshgroup (as [r00t2.io/sysutils/pdsh/dshgroup]) but additional/all other
|
||||||
host list modules are planned.
|
host list modules are planned.
|
||||||
|
|
||||||
For details, see:
|
This package deviates slightly from PDSH in some areas; allowing for more loose or more strict behavior occasionally.
|
||||||
|
Whenever a deviation is offered, this package allows for configuring the generator to behave exactly like PDSH instead
|
||||||
|
(if the deviating behavior is enabled by default).
|
||||||
|
|
||||||
- https://github.com/chaos/pdsh/
|
For details, see the [chaos/pdsh GitHub], the associated [MAN page source], and/or the [rendered MAN page] (via ManKier).
|
||||||
- https://github.com/chaos/pdsh/blob/master/doc/pdsh.1.in
|
You may also want to see the ManKier rendered MAN pages for the [pdsh package].
|
||||||
|
|
||||||
|
[chaos/pdsh GitHub]: https://github.com/chaos/pdsh/
|
||||||
|
[MAN page source]: https://github.com/chaos/pdsh/blob/master/doc/pdsh.1.in
|
||||||
|
[rendered MAN page]: https://www.mankier.com/1/pdsh
|
||||||
|
[pdsh package]: https://www.mankier.com/package/pdsh
|
||||||
*/
|
*/
|
||||||
package pdsh
|
package pdsh
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package pdsh
|
package dshgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -14,5 +14,5 @@ const (
|
|||||||
var (
|
var (
|
||||||
dshGrpDefGrpDir string = "/etc/dsh/group"
|
dshGrpDefGrpDir string = "/etc/dsh/group"
|
||||||
dshGrpInclPtrn *remap.ReMap = &remap.ReMap{Regexp: regexp.MustCompile(`^\s*#include\s+(?P<incl>.+)$`)}
|
dshGrpInclPtrn *remap.ReMap = &remap.ReMap{Regexp: regexp.MustCompile(`^\s*#include\s+(?P<incl>.+)$`)}
|
||||||
dshGrpSubTokenPtrn *remap.ReMap = &remap.ReMap{Regexp: regexp.MustCompile(`^(?P<start_pad>0*)(?P<start>[1-9]+[0-9]*)?(?:-(?P<end_pad>0*)(?P<end>[1-9]+[0-9]*))?$`)}
|
dshGrpSubTokenPtrn *remap.ReMap = &remap.ReMap{Regexp: regexp.MustCompile(`^(?P<start_pad>0+)?(?P<start>[0-9]+)(-(?P<end_pad>0+)?(?P<end>[0-9]+))?$`)}
|
||||||
)
|
)
|
30
pdsh/dshgroup/docs.go
Normal file
30
pdsh/dshgroup/docs.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
Package dshgroup implements so-called "DSH (Dancer's SHell) Group" files.
|
||||||
|
|
||||||
|
It is equivalent to PDSH's [misc/dshgroup] module. ([source])
|
||||||
|
|
||||||
|
Be sure to read the [HOSTLIST EXPRESSIONS] section in the MAN page.
|
||||||
|
|
||||||
|
# Notable Differences
|
||||||
|
|
||||||
|
* This package allows for *never* reading the DSHGROUP_PATH env var (PDSH always reads it) via the "NoEnv" option.
|
||||||
|
|
||||||
|
* This package allows for not adding /etc/dsh/group/<group> files by default via the "NoDefault" option.
|
||||||
|
|
||||||
|
* This package allows for not adding ~/.dsh/group/<group> files by default via the "NoHome" option.
|
||||||
|
|
||||||
|
* This package allows for a "ForceLegacy" mode, disabled by default, that DISABLES the PDSH
|
||||||
|
extension for "#include <path/group>" extension.
|
||||||
|
If ForceLegacy is enabled, "#include ..." lines will be treated as comment lines (ignored) instead.
|
||||||
|
|
||||||
|
* This package allows for whitespace between group patterns. This can be disabled by the "StrictWhitespace" option.
|
||||||
|
|
||||||
|
# TODO/WIP/Not Yet Implemented
|
||||||
|
|
||||||
|
This package is not yet complete.
|
||||||
|
|
||||||
|
[misc/dshgroup]: https://www.mankier.com/1/pdsh#dshgroup_module_options
|
||||||
|
[source]: https://github.com/chaos/pdsh/blob/master/src/modules/dshgroup.c
|
||||||
|
[HOSTLIST EXPRESSIONS]: https://www.mankier.com/1/pdsh#Hostlist_Expressions
|
||||||
|
*/
|
||||||
|
package dshgroup
|
@ -1,10 +1,11 @@
|
|||||||
package pdsh
|
package dshgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ErrEmptyDshGroupTok error = errors.New("empty dsh group pattern token")
|
||||||
ErrInvalidDshGrpSyntax error = errors.New("invalid dsh group file syntax")
|
ErrInvalidDshGrpSyntax error = errors.New("invalid dsh group file syntax")
|
||||||
ErrInvalidDshGrpPtrn error = errors.New("invalid dsh group pattern syntax")
|
ErrInvalidDshGrpPtrn error = errors.New("invalid dsh group pattern syntax")
|
||||||
)
|
)
|
@ -1,4 +1,4 @@
|
|||||||
package pdsh
|
package dshgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@ -170,5 +170,7 @@ For example, assuming the group name `<GROUP>`, the following files will be chec
|
|||||||
*/
|
*/
|
||||||
func (d *DshGroupLister) GroupedHosts(dedupe bool, searchPaths ...string) (groupedHosts map[string][]string, err error) {
|
func (d *DshGroupLister) GroupedHosts(dedupe bool, searchPaths ...string) (groupedHosts map[string][]string, err error) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package pdsh
|
package dshgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@ -13,8 +13,10 @@ import (
|
|||||||
/*
|
/*
|
||||||
ParseDshPtrn parses ptrn using the DSH group pattern ptrn as according to `HOSTLIST EXPRESSSIONS` in pdsh(1).
|
ParseDshPtrn parses ptrn using the DSH group pattern ptrn as according to `HOSTLIST EXPRESSSIONS` in pdsh(1).
|
||||||
`#include` directives are explicitly skipped; this only parses actual generation pattern strings.
|
`#include` directives are explicitly skipped; this only parses actual generation pattern strings.
|
||||||
|
|
||||||
|
The returning generator may either be iterated over with `range` or have `Hosts()` called explicitly. // TODO
|
||||||
*/
|
*/
|
||||||
func ParseDshPtrn(ptrn string) (hostList []string, err error) {
|
func ParseDshPtrn(ptrn string) (generator *DshGrpGenerator, err error) {
|
||||||
|
|
||||||
var r rune
|
var r rune
|
||||||
var pos int
|
var pos int
|
||||||
@ -24,7 +26,10 @@ func ParseDshPtrn(ptrn string) (hostList []string, err error) {
|
|||||||
var tok dshGrpToken
|
var tok dshGrpToken
|
||||||
var strBuf *bytes.Buffer = new(bytes.Buffer)
|
var strBuf *bytes.Buffer = new(bytes.Buffer)
|
||||||
var tokBuf *bytes.Buffer = new(bytes.Buffer)
|
var tokBuf *bytes.Buffer = new(bytes.Buffer)
|
||||||
var parser *dshGrpGenerator = &dshGrpGenerator{
|
|
||||||
|
// TODO: users can be specified per-pattern.
|
||||||
|
|
||||||
|
generator = &DshGrpGenerator{
|
||||||
tokens: make([]dshGrpToken, 0),
|
tokens: make([]dshGrpToken, 0),
|
||||||
tokenized: make([]string, 0),
|
tokenized: make([]string, 0),
|
||||||
text: ptrn,
|
text: ptrn,
|
||||||
@ -50,39 +55,44 @@ func ParseDshPtrn(ptrn string) (hostList []string, err error) {
|
|||||||
if inToken {
|
if inToken {
|
||||||
// Nested [...[
|
// Nested [...[
|
||||||
err = &PtrnParseErr{
|
err = &PtrnParseErr{
|
||||||
pos: uint(pos),
|
pos: uint(pos),
|
||||||
ptrn: ptrn,
|
ptrn: ptrn,
|
||||||
r: r,
|
r: r,
|
||||||
err: ErrInvalidDshGrpSyntax,
|
err: ErrInvalidDshGrpSyntax,
|
||||||
|
inToken: inToken,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parser.tokenized = append(parser.tokenized, strBuf.String())
|
generator.tokenized = append(generator.tokenized, strBuf.String())
|
||||||
strBuf.Reset()
|
strBuf.Reset()
|
||||||
inToken = true
|
inToken = true
|
||||||
case ']':
|
case ']':
|
||||||
if !inToken {
|
if !inToken {
|
||||||
// Nested ]...]
|
// Nested ]...]
|
||||||
err = &PtrnParseErr{
|
err = &PtrnParseErr{
|
||||||
pos: uint(pos),
|
pos: uint(pos),
|
||||||
ptrn: ptrn,
|
ptrn: ptrn,
|
||||||
r: r,
|
r: r,
|
||||||
err: ErrInvalidDshGrpSyntax,
|
err: ErrInvalidDshGrpSyntax,
|
||||||
|
inToken: inToken,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tokStr = tokBuf.String()
|
tokStr = tokBuf.String()
|
||||||
if tok, err = parseDshGrpToken(tokStr); err != nil {
|
if tok, err = parseDshGrpToken(tokStr); err != nil {
|
||||||
err = &PtrnParseErr{
|
err = &PtrnParseErr{
|
||||||
pos: uint(pos),
|
pos: uint(pos),
|
||||||
ptrn: ptrn,
|
ptrn: ptrn,
|
||||||
r: r,
|
r: r,
|
||||||
err: err,
|
err: err,
|
||||||
|
inToken: inToken,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parser.tokens = append(parser.tokens, tok)
|
generator.tokens = append(generator.tokens, tok)
|
||||||
tokBuf.Reset()
|
tokBuf.Reset()
|
||||||
|
// Don't forget the empty element placeholder.
|
||||||
|
generator.tokenized = append(generator.tokenized, "")
|
||||||
inToken = false
|
inToken = false
|
||||||
default:
|
default:
|
||||||
if inToken {
|
if inToken {
|
||||||
@ -90,30 +100,33 @@ func ParseDshPtrn(ptrn string) (hostList []string, err error) {
|
|||||||
if !(0x30 <= r && r <= 0x39) && (r != 0x2d) && (r != 0x2c) {
|
if !(0x30 <= r && r <= 0x39) && (r != 0x2d) && (r != 0x2c) {
|
||||||
// It's not a valid token. (The actual syntax is validated in parseDshGrpToken and parseDshGrpSubtoken)
|
// It's not a valid token. (The actual syntax is validated in parseDshGrpToken and parseDshGrpSubtoken)
|
||||||
err = &PtrnParseErr{
|
err = &PtrnParseErr{
|
||||||
pos: uint(pos),
|
pos: uint(pos),
|
||||||
ptrn: ptrn,
|
ptrn: ptrn,
|
||||||
r: r,
|
r: r,
|
||||||
err: ErrInvalidDshGrpSyntax,
|
err: ErrInvalidDshGrpSyntax,
|
||||||
|
inToken: inToken,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tokBuf.WriteRune(r)
|
tokBuf.WriteRune(r)
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: confirm if inline comments and/or trailing/leading whitespace are handled by pdsh?
|
||||||
if strings.TrimSpace(string(r)) == "" || r == '#' {
|
if strings.TrimSpace(string(r)) == "" || r == '#' {
|
||||||
// Whitespace is "invalid" (treat it as the end of the pattern).
|
// Whitespace is "invalid" (treat it as the end of the pattern).
|
||||||
// Same for end-of-line octothorpes.
|
// Same for end-of-line octothorpes.
|
||||||
if tokBuf.Len() > 0 {
|
if tokBuf.Len() > 0 {
|
||||||
// This should never happen.
|
// This should never happen.
|
||||||
err = &PtrnParseErr{
|
err = &PtrnParseErr{
|
||||||
pos: uint(pos),
|
pos: uint(pos),
|
||||||
ptrn: ptrn,
|
ptrn: ptrn,
|
||||||
r: r,
|
r: r,
|
||||||
err: ErrInvalidDshGrpSyntax,
|
err: ErrInvalidDshGrpSyntax,
|
||||||
|
inToken: inToken,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strBuf.Len() > 0 {
|
if strBuf.Len() > 0 {
|
||||||
parser.tokenized = append(parser.tokenized, strBuf.String())
|
generator.tokenized = append(generator.tokenized, strBuf.String())
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -122,12 +135,13 @@ func ParseDshPtrn(ptrn string) (hostList []string, err error) {
|
|||||||
(r != 0x2d) && // '-'
|
(r != 0x2d) && // '-'
|
||||||
(r != 0x2e) && // '.'
|
(r != 0x2e) && // '.'
|
||||||
!(0x41 <= r && r <= 0x5a) && // 'A' through 'Z' (inclusive)
|
!(0x41 <= r && r <= 0x5a) && // 'A' through 'Z' (inclusive)
|
||||||
!(0x6a <= r && r <= 0x7a) { // 'a' through 'z' (inclusive)
|
!(0x61 <= r && r <= 0x7a) { // 'a' through 'z' (inclusive)
|
||||||
err = &PtrnParseErr{
|
err = &PtrnParseErr{
|
||||||
pos: uint(pos),
|
pos: uint(pos),
|
||||||
ptrn: ptrn,
|
ptrn: ptrn,
|
||||||
r: r,
|
r: r,
|
||||||
err: ErrInvalidDshGrpPtrn,
|
err: ErrInvalidDshGrpPtrn,
|
||||||
|
inToken: inToken,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -154,6 +168,10 @@ func parseDshGrpToken(tokenStr string) (token dshGrpToken, err error) {
|
|||||||
var sub dshGrpSubtoken
|
var sub dshGrpSubtoken
|
||||||
|
|
||||||
s = strings.TrimSpace(tokenStr)
|
s = strings.TrimSpace(tokenStr)
|
||||||
|
if s == "" {
|
||||||
|
err = ErrEmptyDshGroupTok
|
||||||
|
return
|
||||||
|
}
|
||||||
st = strings.Split(s, ",")
|
st = strings.Split(s, ",")
|
||||||
token = dshGrpToken{
|
token = dshGrpToken{
|
||||||
token: tokenStr,
|
token: tokenStr,
|
||||||
@ -190,21 +208,14 @@ func parseDshGrpSubtoken(subTokenStr string) (subtoken dshGrpSubtoken, err error
|
|||||||
if vals = matches["start_pad"]; vals != nil && len(vals) == 1 {
|
if vals = matches["start_pad"]; vals != nil && len(vals) == 1 {
|
||||||
startPad = vals[0]
|
startPad = vals[0]
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
Due to a... particular quirk in the regex that I'm too tired to fix,
|
|
||||||
the start_pad may be e.g. "0" (or "00", etc.) and start may be "" if the range starts *at* 0
|
|
||||||
(or 00, 000, etc.).
|
|
||||||
*/
|
|
||||||
if vals = matches["start"]; vals != nil && len(vals) == 1 {
|
if vals = matches["start"]; vals != nil && len(vals) == 1 {
|
||||||
if u64, err = strconv.ParseUint(vals[0], 10, 64); err != nil {
|
if u64, err = strconv.ParseUint(vals[0], 10, 64); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st.start = uint(u64)
|
st.start = uint(u64)
|
||||||
} else if startPad != "" {
|
|
||||||
// Yeah, regex bug. So we remove one 0 from startPad, and set st.start to 0.
|
|
||||||
st.start = 0 // This is implicit, though.
|
|
||||||
startPad = startPad[:len(startPad)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if vals = matches["end_pad"]; vals != nil && len(vals) == 1 {
|
if vals = matches["end_pad"]; vals != nil && len(vals) == 1 {
|
||||||
endPad = vals[0]
|
endPad = vals[0]
|
||||||
}
|
}
|
59
pdsh/dshgroup/funcs_dshgrp_test.go
Normal file
59
pdsh/dshgroup/funcs_dshgrp_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package dshgroup
|
||||||
|
|
||||||
|
import (
|
||||||
|
`log`
|
||||||
|
`testing`
|
||||||
|
|
||||||
|
`github.com/davecgh/go-spew/spew`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseDshPtrn(t *testing.T) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var idx int
|
||||||
|
var s string
|
||||||
|
var generator *DshGrpGenerator
|
||||||
|
var hostList []string
|
||||||
|
var tgtList []string = []string{
|
||||||
|
"0foo1bar46004quux", "0foo1bar46005quux", "0foo1bar46006quux", "0foo1bar46007quux", "0foo1bar46008quux", "0foo1bar46009quux",
|
||||||
|
"0foo1bar4615quux", "0foo1bar47004quux", "0foo1bar47005quux", "0foo1bar47006quux", "0foo1bar47007quux", "0foo1bar47008quux",
|
||||||
|
"0foo1bar47009quux", "0foo1bar4715quux", "0foo2bar46004quux", "0foo2bar46005quux", "0foo2bar46006quux", "0foo2bar46007quux",
|
||||||
|
"0foo2bar46008quux", "0foo2bar46009quux", "0foo2bar4615quux", "0foo2bar47004quux", "0foo2bar47005quux", "0foo2bar47006quux",
|
||||||
|
"0foo2bar47007quux", "0foo2bar47008quux", "0foo2bar47009quux", "0foo2bar4715quux", "0foo3bar46004quux", "0foo3bar46005quux",
|
||||||
|
"0foo3bar46006quux", "0foo3bar46007quux", "0foo3bar46008quux", "0foo3bar46009quux", "0foo3bar4615quux", "0foo3bar47004quux",
|
||||||
|
"0foo3bar47005quux", "0foo3bar47006quux", "0foo3bar47007quux", "0foo3bar47008quux", "0foo3bar47009quux", "0foo3bar4715quux",
|
||||||
|
"1foo1bar46004quux", "1foo1bar46005quux", "1foo1bar46006quux", "1foo1bar46007quux", "1foo1bar46008quux", "1foo1bar46009quux",
|
||||||
|
"1foo1bar4615quux", "1foo1bar47004quux", "1foo1bar47005quux", "1foo1bar47006quux", "1foo1bar47007quux", "1foo1bar47008quux",
|
||||||
|
"1foo1bar47009quux", "1foo1bar4715quux", "1foo2bar46004quux", "1foo2bar46005quux", "1foo2bar46006quux", "1foo2bar46007quux",
|
||||||
|
"1foo2bar46008quux", "1foo2bar46009quux", "1foo2bar4615quux", "1foo2bar47004quux", "1foo2bar47005quux", "1foo2bar47006quux",
|
||||||
|
"1foo2bar47007quux", "1foo2bar47008quux", "1foo2bar47009quux", "1foo2bar4715quux", "1foo3bar46004quux", "1foo3bar46005quux",
|
||||||
|
"1foo3bar46006quux", "1foo3bar46007quux", "1foo3bar46008quux", "1foo3bar46009quux", "1foo3bar4615quux", "1foo3bar47004quux",
|
||||||
|
"1foo3bar47005quux", "1foo3bar47006quux", "1foo3bar47007quux", "1foo3bar47008quux", "1foo3bar47009quux", "1foo3bar4715quux",
|
||||||
|
"2foo1bar46004quux", "2foo1bar46005quux", "2foo1bar46006quux", "2foo1bar46007quux", "2foo1bar46008quux", "2foo1bar46009quux",
|
||||||
|
"2foo1bar4615quux", "2foo1bar47004quux", "2foo1bar47005quux", "2foo1bar47006quux", "2foo1bar47007quux", "2foo1bar47008quux",
|
||||||
|
"2foo1bar47009quux", "2foo1bar4715quux", "2foo2bar46004quux", "2foo2bar46005quux", "2foo2bar46006quux", "2foo2bar46007quux",
|
||||||
|
"2foo2bar46008quux", "2foo2bar46009quux", "2foo2bar4615quux", "2foo2bar47004quux", "2foo2bar47005quux", "2foo2bar47006quux",
|
||||||
|
"2foo2bar47007quux", "2foo2bar47008quux", "2foo2bar47009quux", "2foo2bar4715quux", "2foo3bar46004quux", "2foo3bar46005quux",
|
||||||
|
"2foo3bar46006quux", "2foo3bar46007quux", "2foo3bar46008quux", "2foo3bar46009quux", "2foo3bar4615quux", "2foo3bar47004quux",
|
||||||
|
"2foo3bar47005quux", "2foo3bar47006quux", "2foo3bar47007quux", "2foo3bar47008quux", "2foo3bar47009quux", "2foo3bar4715quux",
|
||||||
|
}
|
||||||
|
|
||||||
|
if generator, err = ParseDshPtrn("[0-2]foo[1-3]bar[4][6-7]baz[004-009,15]quux"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = spew.Sdump(generator)
|
||||||
|
|
||||||
|
hostList = generator.Hosts()
|
||||||
|
t.Log(hostList)
|
||||||
|
|
||||||
|
if len(hostList) != len(tgtList) {
|
||||||
|
t.Fatalf("Generated list length (%d) does not match target (%d)", len(hostList), len(tgtList))
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, s = range hostList {
|
||||||
|
if s != tgtList[idx] {
|
||||||
|
log.Fatalf("Test vector %d ('%s') does not match generated value '%s'", idx+1, tgtList[idx], s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
pdsh/dshgroup/funcs_dshgrpgenerator.go
Normal file
36
pdsh/dshgroup/funcs_dshgrpgenerator.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package dshgroup
|
||||||
|
|
||||||
|
func (d *DshGrpGenerator) Generate() (yieldFunc func(yield func(host string) (done bool))) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DshGrpGenerator) Hosts() (hostList []string) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DshGrpGenerator) Host() (host string) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DshGrpGenerator) Next() (done bool) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DshGrpGenerator) Reset() {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package pdsh
|
package dshgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -8,8 +8,8 @@ import (
|
|||||||
func (p *PtrnParseErr) Error() (errStr string) {
|
func (p *PtrnParseErr) Error() (errStr string) {
|
||||||
|
|
||||||
errStr = fmt.Sprintf(
|
errStr = fmt.Sprintf(
|
||||||
"Parse error in pattern '%s', position %d rune '%s': %v",
|
"Parse error in pattern '%s', position %d rune '%s' (%#x) (in token: %v): %v",
|
||||||
p.ptrn, p.pos, string(p.r), p.err,
|
p.ptrn, p.pos, string(p.r), p.r, p.inToken, p.err,
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
90
pdsh/dshgroup/types.go
Normal file
90
pdsh/dshgroup/types.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package dshgroup
|
||||||
|
|
||||||
|
// TODO: This... doesn't really have much usefulness, does it?
|
||||||
|
/*
|
||||||
|
type (
|
||||||
|
HostLister interface {
|
||||||
|
// Hosts returns ALL hsots (where applicable) that are considered/generated for a Lister.
|
||||||
|
Hosts() (hosts []string, err error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
type (
|
||||||
|
/*
|
||||||
|
DshGroupLister behaves like the host list generator
|
||||||
|
for pdsh(1)'s "dshgroup module options" (the `misc/dshgroup`
|
||||||
|
module for pdsh).
|
||||||
|
*/
|
||||||
|
DshGroupLister struct {
|
||||||
|
/*
|
||||||
|
NoEnv, if true, will *not* use DSHGROUP_PATH (force-defaulting to /etc/dsh/group/,
|
||||||
|
but see NoDefault).
|
||||||
|
*/
|
||||||
|
NoEnv bool
|
||||||
|
/*
|
||||||
|
NoDefault, if true, will *not* add the default path `/etc/dsh/group/`
|
||||||
|
to the search paths.
|
||||||
|
|
||||||
|
If NoDefault is false, this path is only added if DSHGROUP_PATH is not defined
|
||||||
|
(or, if it IS defined, if NoEnv is true).
|
||||||
|
*/
|
||||||
|
NoDefault bool
|
||||||
|
// NoHome, if true, will *not* add the `~/.dsh/group/` path to the search paths.
|
||||||
|
NoHome bool
|
||||||
|
/*
|
||||||
|
ForceLegacy, if true, will disable the PDSH `#include <PATH|GROUP>` modification --
|
||||||
|
treating the source as a traditional DSH group file instead (e.g. `#include ...`
|
||||||
|
is treated as just a comment).
|
||||||
|
*/
|
||||||
|
ForceLegacy bool
|
||||||
|
// StrictWhitespace follows the same behavior as PDSH regarding no whitespace between patterns.
|
||||||
|
StrictWhitespace bool
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// DshGrpGenerator generates a list of hosts according to the pdsh "dshgroup" module.
|
||||||
|
DshGrpGenerator struct {
|
||||||
|
/*
|
||||||
|
tokens are interleaved with tokenized and indexed *after*;
|
||||||
|
in other words, str = <substr0><token0><substr1><token1>...
|
||||||
|
*/
|
||||||
|
tokens []dshGrpToken
|
||||||
|
// tokenized holds the split original text with tokens removed and split where the tokens occur.
|
||||||
|
tokenized []string
|
||||||
|
// text holds the original pattern.
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
dshGrpToken struct {
|
||||||
|
/*
|
||||||
|
token contains the original range specifier.
|
||||||
|
Tokens may be e.g.:
|
||||||
|
|
||||||
|
* 3: str3
|
||||||
|
* 3-5: str3, str4, str5
|
||||||
|
* 3,5: str3, str5
|
||||||
|
*/
|
||||||
|
token string
|
||||||
|
// subtokens hold a split of the individual range specifiers.
|
||||||
|
subtokens []dshGrpSubtoken
|
||||||
|
}
|
||||||
|
dshGrpSubtoken struct {
|
||||||
|
// start indicates either the single value or the start of the range.
|
||||||
|
start uint
|
||||||
|
// end, if 0 or less than start, indicates a single-value range.
|
||||||
|
end uint
|
||||||
|
// pad, if non-empty, is a string to add to the beginning of each of the generated substrings for this subtoken.
|
||||||
|
pad string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
PtrnParseErr struct {
|
||||||
|
pos uint
|
||||||
|
ptrn string
|
||||||
|
r rune
|
||||||
|
err error
|
||||||
|
inToken bool
|
||||||
|
}
|
||||||
|
)
|
11
pdsh/genders/docs.go
Normal file
11
pdsh/genders/docs.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Package genders implements the [misc/genders] PDSH module. ([source])
|
||||||
|
|
||||||
|
# TODO/WIP/Not Yet Implemented
|
||||||
|
|
||||||
|
This package is not yet complete.
|
||||||
|
|
||||||
|
[misc/genders]: https://www.mankier.com/1/pdsh#genders_module_options
|
||||||
|
[source]: https://github.com/chaos/pdsh/blob/master/src/modules/genders.c
|
||||||
|
*/
|
||||||
|
package genders
|
11
pdsh/machines/docs.go
Normal file
11
pdsh/machines/docs.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Package machines implements the [misc/machines] PDSH module. ([source])
|
||||||
|
|
||||||
|
# TODO/WIP/Not Yet Implemented
|
||||||
|
|
||||||
|
This package is not yet complete.
|
||||||
|
|
||||||
|
[misc/machines]: https://www.mankier.com/1/pdsh#machines_module_options
|
||||||
|
[source]: https://github.com/chaos/pdsh/blob/master/src/modules/machines.c
|
||||||
|
*/
|
||||||
|
package machines
|
11
pdsh/netgroup/docs.go
Normal file
11
pdsh/netgroup/docs.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Package netgroup implements the [misc/netgroup] PDSH module. ([source])
|
||||||
|
|
||||||
|
# TODO/WIP/Not Yet Implemented
|
||||||
|
|
||||||
|
This package is not yet complete.
|
||||||
|
|
||||||
|
[misc/netgroup]: https://www.mankier.com/1/pdsh#netgroup_module_options
|
||||||
|
[source]: https://github.com/chaos/pdsh/blob/master/src/modules/netgroup.c
|
||||||
|
*/
|
||||||
|
package netgroup
|
11
pdsh/nodeupdown/docs.go
Normal file
11
pdsh/nodeupdown/docs.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Package nodeupdown implements the [misc/nodeupdown] PDSH module. ([source])
|
||||||
|
|
||||||
|
# TODO/WIP/Not Yet Implemented
|
||||||
|
|
||||||
|
This package is not yet complete.
|
||||||
|
|
||||||
|
[misc/nodeupdown]: https://www.mankier.com/1/pdsh#nodeupdown_module_options
|
||||||
|
[source]: https://github.com/chaos/pdsh/blob/master/src/modules/nodeupdown.c
|
||||||
|
*/
|
||||||
|
package nodeupdown
|
11
pdsh/slurm/docs.go
Normal file
11
pdsh/slurm/docs.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Package slurm implements the [misc/slurm] PDSH module. ([source])
|
||||||
|
|
||||||
|
# TODO/WIP/Not Yet Implemented
|
||||||
|
|
||||||
|
This package is not yet complete.
|
||||||
|
|
||||||
|
[misc/slurm]: https://www.mankier.com/1/pdsh#slurm_module_options
|
||||||
|
[source]: https://github.com/chaos/pdsh/blob/master/src/modules/slurm.c
|
||||||
|
*/
|
||||||
|
package slurm
|
11
pdsh/torque/docs.go
Normal file
11
pdsh/torque/docs.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Package torque implements the [misc/torque] PDSH module. ([source])
|
||||||
|
|
||||||
|
# TODO/WIP/Not Yet Implemented
|
||||||
|
|
||||||
|
This package is not yet complete.
|
||||||
|
|
||||||
|
[misc/torque]: https://www.mankier.com/1/pdsh#torque_module_options
|
||||||
|
[source]: https://github.com/chaos/pdsh/blob/master/src/modules/torque.c
|
||||||
|
*/
|
||||||
|
package torque
|
122
pdsh/types.go
122
pdsh/types.go
@ -1,86 +1,60 @@
|
|||||||
package pdsh
|
package pdsh
|
||||||
|
|
||||||
// TODO: This... doesn't really have much usefulness, does it?
|
|
||||||
/*
|
|
||||||
type (
|
|
||||||
HostLister interface {
|
|
||||||
// Hosts returns ALL hsots (where applicable) that are considered/generated for a Lister.
|
|
||||||
Hosts() (hosts []string, err error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
/*
|
/*
|
||||||
DshGroupLister behaves like the host list generator
|
Generator is one of the PDSH host generators/iterators offered by this module.
|
||||||
for pdsh(1)'s "dshgroup module options" (the `misc/dshgroup`
|
|
||||||
module for pdsh).
|
Note that these generators/iterators are *stateful*, which means they shouldn't
|
||||||
|
(probably; I'm not your dad) be used concurrently (unless you want some hard-to-debug results)
|
||||||
|
and all methods advance the generator - so you probably don't want to call both Generate() and
|
||||||
|
Next()/Host() on the same instance, for example.
|
||||||
*/
|
*/
|
||||||
DshGroupLister struct {
|
Generator interface {
|
||||||
/*
|
/*
|
||||||
NoEnv, if true, will *not* use DSHGROUP_PATH (force-defaulting to /etc/dsh/group/,
|
Generate provides a Go-native iterator (also called a "RangeFunc" or "range over function type")
|
||||||
but see NoDefault).
|
as found in Go 1.23 onwards.
|
||||||
*/
|
|
||||||
NoEnv bool
|
|
||||||
/*
|
|
||||||
NoDefault, if true, will *not* add the default path `/etc/dsh/group/`
|
|
||||||
to the search paths.
|
|
||||||
|
|
||||||
If NoDefault is false, this path is only added if DSHGROUP_PATH is not defined
|
See the assocaied blog entry for details: https://go.dev/blog/range-functions
|
||||||
(or, if it IS defined, if NoEnv is true).
|
|
||||||
|
Essentially it allows for e.g.:
|
||||||
|
|
||||||
|
for host := range (Generator).Generate() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
which is the "new standard" approach for iteration.
|
||||||
*/
|
*/
|
||||||
NoDefault bool
|
Generate() (yieldFunc func(yield func(host string) (done bool)))
|
||||||
// NoHome, if true, will *not* add the `~/.dsh/group/` path to the search paths.
|
|
||||||
NoHome bool
|
|
||||||
/*
|
/*
|
||||||
ForceLegacy, if true, will disable the PDSH `#include <PATH|GROUP>` modification --
|
Reset is used to reset a Generator, allowing one to "restart" the generation at the beginning.
|
||||||
treating the source as a traditional DSH group file instead (e.g. `#include ...`
|
|
||||||
is treated as just a comment).
|
Generators in this module are generally single-use, but can be reset/reused with this method.
|
||||||
*/
|
*/
|
||||||
ForceLegacy bool
|
Reset()
|
||||||
}
|
/*
|
||||||
)
|
Hosts returns a complete generated hostlist at once if you'd rather not iterate.
|
||||||
|
|
||||||
type (
|
Hosts() *does* perform an iteration in runtime, so the recommendation against concurrency
|
||||||
dshGrpGenerator struct {
|
stands, but it calls Reset() when done generating to allow other methods of a Generator to be used.
|
||||||
/*
|
*/
|
||||||
tokens are interleaved with tokenized and indexed *after*;
|
Hosts() (hostList []string)
|
||||||
in other words, str = <substr0><token0><substr1><token1>...
|
/*
|
||||||
*/
|
Next and Host behave like more "traditional" iterators, e.g. like (database/sql).Row.Next().
|
||||||
tokens []dshGrpToken
|
|
||||||
// tokenized holds the split original text with tokens removed and split where the tokens occur.
|
Next advances the internal state to the next host, and Host() returns it.
|
||||||
tokenized []string
|
*/
|
||||||
// text holds the original pattern.
|
Next() (done bool)
|
||||||
text string
|
/*
|
||||||
}
|
Host returns the current host string (or "" if done).
|
||||||
dshGrpToken struct {
|
|
||||||
/*
|
Be sure to e.g.:
|
||||||
token contains the original range specifier.
|
|
||||||
Tokens may be e.g.:
|
for (Generator).Next() {
|
||||||
|
host := (Generator).Host()
|
||||||
* 3: str3
|
}
|
||||||
* 3-5: str3, str4, str5
|
|
||||||
* 3,5: str3, str5
|
otherwise the Host return value will not change.
|
||||||
*/
|
*/
|
||||||
token string
|
Host() (host string)
|
||||||
// subtokens hold a split of the individual range specifiers.
|
|
||||||
subtokens []dshGrpSubtoken
|
|
||||||
}
|
|
||||||
dshGrpSubtoken struct {
|
|
||||||
// start indicates either the single value or the start of the range.
|
|
||||||
start uint
|
|
||||||
// end, if 0 or less than start, indicates a single-value range.
|
|
||||||
end uint
|
|
||||||
// pad, if non-empty, is a string to add to the beginning of each of the generated substrings for this subtoken.
|
|
||||||
pad string
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
PtrnParseErr struct {
|
|
||||||
pos uint
|
|
||||||
ptrn string
|
|
||||||
r rune
|
|
||||||
err error
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
14
pdsh/wcoll/docs.go
Normal file
14
pdsh/wcoll/docs.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Package wcoll implements the "default" [WCOLL] method for PDSH. ([source])
|
||||||
|
|
||||||
|
Be sure to read the [HOSTLIST EXPRESSIONS] section in the MAN page.
|
||||||
|
|
||||||
|
# TODO/WIP/Not Yet Implemented
|
||||||
|
|
||||||
|
This package is not yet complete.
|
||||||
|
|
||||||
|
[WCOLL]: https://www.mankier.com/1/pdsh#Environment_Variables
|
||||||
|
[source]: https://github.com/chaos/pdsh/blob/master/src/pdsh/wcoll.c
|
||||||
|
[HOSTLIST EXPRESSIONS]: https://www.mankier.com/1/pdsh#Hostlist_Expressions
|
||||||
|
*/
|
||||||
|
package wcoll
|
Loading…
x
Reference in New Issue
Block a user