github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/command/server_members.go (about)

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