command/members: Improve output. Fixes #143

This commit is contained in:
Armon Dadgar 2014-06-06 16:00:02 -07:00
parent b144633815
commit e9c7098936
2 changed files with 78 additions and 26 deletions

View File

@ -25,8 +25,7 @@ Usage: consul members [options]
Options: Options:
-role=<regexp> If provided, output is filtered to only nodes matching -detailed Provides detailed information about nodes
the regular expression for role
-rpc-addr=127.0.0.1:8400 RPC address of the Consul agent. -rpc-addr=127.0.0.1:8400 RPC address of the Consul agent.
@ -40,12 +39,13 @@ Options:
} }
func (c *MembersCommand) Run(args []string) int { func (c *MembersCommand) Run(args []string) int {
var detailed bool
var wan bool var wan bool
var roleFilter, statusFilter string var statusFilter string
cmdFlags := flag.NewFlagSet("members", flag.ContinueOnError) cmdFlags := flag.NewFlagSet("members", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) } cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
cmdFlags.BoolVar(&detailed, "detailed", false, "detailed output")
cmdFlags.BoolVar(&wan, "wan", false, "wan members") cmdFlags.BoolVar(&wan, "wan", false, "wan members")
cmdFlags.StringVar(&roleFilter, "role", ".*", "role filter")
cmdFlags.StringVar(&statusFilter, "status", ".*", "status filter") cmdFlags.StringVar(&statusFilter, "status", ".*", "status filter")
rpcAddr := RPCAddrFlag(cmdFlags) rpcAddr := RPCAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil { if err := cmdFlags.Parse(args); err != nil {
@ -53,11 +53,6 @@ func (c *MembersCommand) Run(args []string) int {
} }
// Compile the regexp // Compile the regexp
roleRe, err := regexp.Compile(roleFilter)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to compile role regexp: %v", err))
return 1
}
statusRe, err := regexp.Compile(statusFilter) statusRe, err := regexp.Compile(statusFilter)
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to compile status regexp: %v", err)) c.Ui.Error(fmt.Sprintf("Failed to compile status regexp: %v", err))
@ -82,13 +77,80 @@ func (c *MembersCommand) Run(args []string) int {
return 1 return 1
} }
result := make([]string, 0, len(members)) // Filter the results
for _, member := range members { n := len(members)
// Skip the non-matching members for i := 0; i < n; i++ {
if !roleRe.MatchString(member.Tags["role"]) || !statusRe.MatchString(member.Status) { member := members[i]
if !statusRe.MatchString(member.Status) {
members[i], members[n-1] = members[n-1], members[i]
i--
n--
continue continue
} }
}
members = members[:n]
// No matching members
if len(members) == 0 {
return 2
}
// Generate the output
var result []string
if detailed {
result = c.detailedOutput(members)
} else {
result = c.standardOutput(members)
}
// Generate the columnized version
output := columnize.SimpleFormat(result)
c.Ui.Output(string(output))
return 0
}
// standardOutput is used to dump the most useful information about nodes
// in a more human-friendly format
func (c *MembersCommand) standardOutput(members []agent.Member) []string {
result := make([]string, 0, len(members))
header := "Node|Address|Status|Type|Build|Protocol"
result = append(result, header)
for _, member := range members {
addr := net.TCPAddr{IP: member.Addr, Port: int(member.Port)}
protocol := member.Tags["vsn"]
build := member.Tags["build"]
if build == "" {
build = "< 0.3"
} else if idx := strings.Index(build, ":"); idx != -1 {
build = build[:idx]
}
switch member.Tags["role"] {
case "node":
line := fmt.Sprintf("%s|%s|%s|client|%s|%s",
member.Name, addr.String(), member.Status, build, protocol)
result = append(result, line)
case "consul":
line := fmt.Sprintf("%s|%s|%s|server|%s|%s",
member.Name, addr.String(), member.Status, build, protocol)
result = append(result, line)
default:
line := fmt.Sprintf("%s|%s|%s|unknown||",
member.Name, addr.String(), member.Status)
result = append(result, line)
}
}
return result
}
// detailedOutput is used to dump all known information about nodes in
// their raw format
func (c *MembersCommand) detailedOutput(members []agent.Member) []string {
result := make([]string, 0, len(members))
header := "Node|Address|Status|Tags"
result = append(result, header)
for _, member := range members {
// Format the tags as tag1=v1,tag2=v2,... // Format the tags as tag1=v1,tag2=v2,...
var tagPairs []string var tagPairs []string
for name, value := range member.Tags { for name, value := range member.Tags {
@ -101,17 +163,7 @@ func (c *MembersCommand) Run(args []string) int {
member.Name, addr.String(), member.Status, tags) member.Name, addr.String(), member.Status, tags)
result = append(result, line) result = append(result, line)
} }
return result
// No matching members
if len(result) == 0 {
return 2
}
// Generate the columnized version
output := columnize.SimpleFormat(result)
c.Ui.Output(string(output))
return 0
} }
func (c *MembersCommand) Synopsis() string { func (c *MembersCommand) Synopsis() string {

View File

@ -22,8 +22,8 @@ Usage: `consul members [options]`
The command-line flags are all optional. The list of available flags are: The command-line flags are all optional. The list of available flags are:
* `-role` - If provided, output is filtered to only nodes matching * `-detailed` - If provided, output shows more detailed information
the regular expression for role about each node.
* `-rpc-addr` - Address to the RPC server of the agent you want to contact * `-rpc-addr` - Address to the RPC server of the agent you want to contact
to send this command. If this isn't specified, the command will contact to send this command. If this isn't specified, the command will contact