github.com/djenriquez/nomad-1@v0.8.1/command/server_members.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"sort"
     7  	"strings"
     8  
     9  	multierror "github.com/hashicorp/go-multierror"
    10  	"github.com/hashicorp/nomad/api"
    11  	"github.com/posener/complete"
    12  	"github.com/ryanuber/columnize"
    13  )
    14  
    15  type ServerMembersCommand struct {
    16  	Meta
    17  }
    18  
    19  func (c *ServerMembersCommand) Help() string {
    20  	helpText := `
    21  Usage: nomad server members [options]
    22  
    23    Display a list of the known servers and their status. Only Nomad servers are
    24    able to service this command.
    25  
    26  General Options:
    27  
    28    ` + generalOptionsUsage() + `
    29  
    30  Server Members Options:
    31  
    32    -detailed
    33      Show detailed information about each member. This dumps
    34      a raw set of tags which shows more information than the
    35      default output format.
    36  `
    37  	return strings.TrimSpace(helpText)
    38  }
    39  
    40  func (c *ServerMembersCommand) AutocompleteFlags() complete.Flags {
    41  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    42  		complete.Flags{
    43  			"-detailed": complete.PredictNothing,
    44  		})
    45  }
    46  
    47  func (c *ServerMembersCommand) AutocompleteArgs() complete.Predictor {
    48  	return complete.PredictNothing
    49  }
    50  
    51  func (c *ServerMembersCommand) Synopsis() string {
    52  	return "Display a list of known servers and their status"
    53  }
    54  
    55  func (c *ServerMembersCommand) Run(args []string) int {
    56  	var detailed bool
    57  
    58  	flags := c.Meta.FlagSet("server-members", FlagSetClient)
    59  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    60  	flags.BoolVar(&detailed, "detailed", false, "Show detailed output")
    61  
    62  	if err := flags.Parse(args); err != nil {
    63  		return 1
    64  	}
    65  
    66  	// Check for extra arguments
    67  	args = flags.Args()
    68  	if len(args) != 0 {
    69  		c.Ui.Error(c.Help())
    70  		return 1
    71  	}
    72  
    73  	// Get the HTTP client
    74  	client, err := c.Meta.Client()
    75  	if err != nil {
    76  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
    77  		return 1
    78  	}
    79  
    80  	// Query the members
    81  	srvMembers, err := client.Agent().Members()
    82  	if err != nil {
    83  		c.Ui.Error(fmt.Sprintf("Error querying servers: %s", err))
    84  		return 1
    85  	}
    86  
    87  	if srvMembers == nil {
    88  		c.Ui.Error("Agent doesn't know about server members")
    89  		return 0
    90  	}
    91  
    92  	// Sort the members
    93  	sort.Sort(api.AgentMembersNameSort(srvMembers.Members))
    94  
    95  	// Determine the leaders per region.
    96  	leaders, leaderErr := regionLeaders(client, srvMembers.Members)
    97  
    98  	// Format the list
    99  	var out []string
   100  	if detailed {
   101  		out = detailedOutput(srvMembers.Members)
   102  	} else {
   103  		out = standardOutput(srvMembers.Members, leaders)
   104  	}
   105  
   106  	// Dump the list
   107  	c.Ui.Output(columnize.SimpleFormat(out))
   108  
   109  	// If there were leader errors display a warning
   110  	if leaderErr != nil {
   111  		c.Ui.Output("")
   112  		c.Ui.Warn(fmt.Sprintf("Error determining leaders: %s", leaderErr))
   113  		return 1
   114  	}
   115  
   116  	return 0
   117  }
   118  
   119  func standardOutput(mem []*api.AgentMember, leaders map[string]string) []string {
   120  	// Format the members list
   121  	members := make([]string, len(mem)+1)
   122  	members[0] = "Name|Address|Port|Status|Leader|Protocol|Build|Datacenter|Region"
   123  	for i, member := range mem {
   124  		reg := member.Tags["region"]
   125  		regLeader, ok := leaders[reg]
   126  		isLeader := false
   127  		if ok {
   128  			if regLeader == net.JoinHostPort(member.Addr, member.Tags["port"]) {
   129  
   130  				isLeader = true
   131  			}
   132  		}
   133  
   134  		members[i+1] = fmt.Sprintf("%s|%s|%d|%s|%t|%d|%s|%s|%s",
   135  			member.Name,
   136  			member.Addr,
   137  			member.Port,
   138  			member.Status,
   139  			isLeader,
   140  			member.ProtocolCur,
   141  			member.Tags["build"],
   142  			member.Tags["dc"],
   143  			member.Tags["region"])
   144  	}
   145  	return members
   146  }
   147  
   148  func detailedOutput(mem []*api.AgentMember) []string {
   149  	// Format the members list
   150  	members := make([]string, len(mem)+1)
   151  	members[0] = "Name|Address|Port|Tags"
   152  	for i, member := range mem {
   153  		// Format the tags
   154  		tagPairs := make([]string, 0, len(member.Tags))
   155  		for k, v := range member.Tags {
   156  			tagPairs = append(tagPairs, fmt.Sprintf("%s=%s", k, v))
   157  		}
   158  		tags := strings.Join(tagPairs, ",")
   159  
   160  		members[i+1] = fmt.Sprintf("%s|%s|%d|%s",
   161  			member.Name,
   162  			member.Addr,
   163  			member.Port,
   164  			tags)
   165  	}
   166  	return members
   167  }
   168  
   169  // regionLeaders returns a map of regions to the IP of the member that is the
   170  // leader.
   171  func regionLeaders(client *api.Client, mem []*api.AgentMember) (map[string]string, error) {
   172  	// Determine the unique regions.
   173  	leaders := make(map[string]string)
   174  	regions := make(map[string]struct{})
   175  	for _, m := range mem {
   176  		// Ignore left members
   177  		// This prevents querying for leader status on regions where all members have left
   178  		if m.Status == "left" {
   179  			continue
   180  		}
   181  
   182  		regions[m.Tags["region"]] = struct{}{}
   183  	}
   184  
   185  	if len(regions) == 0 {
   186  		return leaders, nil
   187  	}
   188  
   189  	var mErr multierror.Error
   190  	status := client.Status()
   191  	for reg := range regions {
   192  		l, err := status.RegionLeader(reg)
   193  		if err != nil {
   194  			multierror.Append(&mErr, fmt.Errorf("Region %q: %v", reg, err))
   195  			continue
   196  		}
   197  
   198  		leaders[reg] = l
   199  	}
   200  
   201  	return leaders, mErr.ErrorOrNil()
   202  }