github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/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 }