github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/command/server_members.go (about)

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