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 }