github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/cmd/juju/status.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "encoding/json" 8 "fmt" 9 10 "launchpad.net/gnuflag" 11 12 "github.com/juju/juju/cmd" 13 "github.com/juju/juju/cmd/envcmd" 14 "github.com/juju/juju/environs/network" 15 "github.com/juju/juju/instance" 16 "github.com/juju/juju/juju" 17 "github.com/juju/juju/state/api" 18 "github.com/juju/juju/state/api/params" 19 "github.com/juju/juju/state/apiserver/client" 20 ) 21 22 type StatusCommand struct { 23 envcmd.EnvCommandBase 24 out cmd.Output 25 patterns []string 26 } 27 28 var statusDoc = ` 29 This command will report on the runtime state of various system entities. 30 31 Service or unit names may be specified to filter the status to only those 32 services and units that match, along with the related machines, services 33 and units. If a subordinate unit is matched, then its principal unit will 34 be displayed. If a principal unit is matched, then all of its subordinates 35 will be displayed. 36 37 Wildcards ('*') may be specified in service/unit names to match any sequence 38 of characters. For example, 'nova-*' will match any service whose name begins 39 with 'nova-': 'nova-compute', 'nova-volume', etc. 40 ` 41 42 func (c *StatusCommand) Info() *cmd.Info { 43 return &cmd.Info{ 44 Name: "status", 45 Args: "[pattern ...]", 46 Purpose: "output status information about an environment", 47 Doc: statusDoc, 48 Aliases: []string{"stat"}, 49 } 50 } 51 52 func (c *StatusCommand) SetFlags(f *gnuflag.FlagSet) { 53 c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{ 54 "yaml": cmd.FormatYaml, 55 "json": cmd.FormatJson, 56 }) 57 } 58 59 func (c *StatusCommand) Init(args []string) error { 60 c.patterns = args 61 return nil 62 } 63 64 var connectionError = `Unable to connect to environment %q. 65 Please check your credentials or use 'juju bootstrap' to create a new environment. 66 67 Error details: 68 %v 69 ` 70 71 func (c *StatusCommand) Run(ctx *cmd.Context) error { 72 // Just verify the pattern validity client side, do not use the matcher 73 _, err := client.NewUnitMatcher(c.patterns) 74 if err != nil { 75 return err 76 } 77 apiclient, err := juju.NewAPIClientFromName(c.EnvName) 78 if err != nil { 79 return fmt.Errorf(connectionError, c.EnvName, err) 80 } 81 defer apiclient.Close() 82 83 status, err := apiclient.Status(c.patterns) 84 // Display any error, but continue to print status if some was returned 85 if err != nil { 86 fmt.Fprintf(ctx.Stderr, "%v\n", err) 87 } 88 result := formatStatus(status) 89 return c.out.Write(ctx, result) 90 } 91 92 type formattedStatus struct { 93 Environment string `json:"environment"` 94 Machines map[string]machineStatus `json:"machines"` 95 Services map[string]serviceStatus `json:"services"` 96 Networks map[string]networkStatus `json:"networks,omitempty" yaml:",omitempty"` 97 } 98 99 type errorStatus struct { 100 StatusError string `json:"status-error" yaml:"status-error"` 101 } 102 103 type machineStatus struct { 104 Err error `json:"-" yaml:",omitempty"` 105 AgentState params.Status `json:"agent-state,omitempty" yaml:"agent-state,omitempty"` 106 AgentStateInfo string `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"` 107 AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"` 108 DNSName string `json:"dns-name,omitempty" yaml:"dns-name,omitempty"` 109 InstanceId instance.Id `json:"instance-id,omitempty" yaml:"instance-id,omitempty"` 110 InstanceState string `json:"instance-state,omitempty" yaml:"instance-state,omitempty"` 111 Life string `json:"life,omitempty" yaml:"life,omitempty"` 112 Series string `json:"series,omitempty" yaml:"series,omitempty"` 113 Id string `json:"-" yaml:"-"` 114 Containers map[string]machineStatus `json:"containers,omitempty" yaml:"containers,omitempty"` 115 Hardware string `json:"hardware,omitempty" yaml:"hardware,omitempty"` 116 HAStatus string `json:"state-server-member-status,omitempty" yaml:"state-server-member-status,omitempty"` 117 } 118 119 // A goyaml bug means we can't declare these types 120 // locally to the GetYAML methods. 121 type machineStatusNoMarshal machineStatus 122 123 func (s machineStatus) MarshalJSON() ([]byte, error) { 124 if s.Err != nil { 125 return json.Marshal(errorStatus{s.Err.Error()}) 126 } 127 return json.Marshal(machineStatusNoMarshal(s)) 128 } 129 130 func (s machineStatus) GetYAML() (tag string, value interface{}) { 131 if s.Err != nil { 132 return "", errorStatus{s.Err.Error()} 133 } 134 // TODO(rog) rename mNoMethods to noMethods (and also in 135 // the other GetYAML methods) when people are using the non-buggy 136 // goyaml version. 137 type mNoMethods machineStatus 138 return "", mNoMethods(s) 139 } 140 141 type serviceStatus struct { 142 Err error `json:"-" yaml:",omitempty"` 143 Charm string `json:"charm" yaml:"charm"` 144 CanUpgradeTo string `json:"can-upgrade-to,omitempty" yaml:"can-upgrade-to,omitempty"` 145 Exposed bool `json:"exposed" yaml:"exposed"` 146 Life string `json:"life,omitempty" yaml:"life,omitempty"` 147 Relations map[string][]string `json:"relations,omitempty" yaml:"relations,omitempty"` 148 Networks map[string][]string `json:"networks,omitempty" yaml:"networks,omitempty"` 149 SubordinateTo []string `json:"subordinate-to,omitempty" yaml:"subordinate-to,omitempty"` 150 Units map[string]unitStatus `json:"units,omitempty" yaml:"units,omitempty"` 151 } 152 153 type serviceStatusNoMarshal serviceStatus 154 155 func (s serviceStatus) MarshalJSON() ([]byte, error) { 156 if s.Err != nil { 157 return json.Marshal(errorStatus{s.Err.Error()}) 158 } 159 type sNoMethods serviceStatus 160 return json.Marshal(sNoMethods(s)) 161 } 162 163 func (s serviceStatus) GetYAML() (tag string, value interface{}) { 164 if s.Err != nil { 165 return "", errorStatus{s.Err.Error()} 166 } 167 type sNoMethods serviceStatus 168 return "", sNoMethods(s) 169 } 170 171 type unitStatus struct { 172 Err error `json:"-" yaml:",omitempty"` 173 Charm string `json:"upgrading-from,omitempty" yaml:"upgrading-from,omitempty"` 174 AgentState params.Status `json:"agent-state,omitempty" yaml:"agent-state,omitempty"` 175 AgentStateInfo string `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"` 176 AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"` 177 Life string `json:"life,omitempty" yaml:"life,omitempty"` 178 Machine string `json:"machine,omitempty" yaml:"machine,omitempty"` 179 OpenedPorts []string `json:"open-ports,omitempty" yaml:"open-ports,omitempty"` 180 PublicAddress string `json:"public-address,omitempty" yaml:"public-address,omitempty"` 181 Subordinates map[string]unitStatus `json:"subordinates,omitempty" yaml:"subordinates,omitempty"` 182 } 183 184 type unitStatusNoMarshal unitStatus 185 186 func (s unitStatus) MarshalJSON() ([]byte, error) { 187 if s.Err != nil { 188 return json.Marshal(errorStatus{s.Err.Error()}) 189 } 190 return json.Marshal(unitStatusNoMarshal(s)) 191 } 192 193 func (s unitStatus) GetYAML() (tag string, value interface{}) { 194 if s.Err != nil { 195 return "", errorStatus{s.Err.Error()} 196 } 197 type uNoMethods unitStatus 198 return "", unitStatusNoMarshal(s) 199 } 200 201 type networkStatus struct { 202 Err error `json:"-" yaml:",omitempty"` 203 ProviderId network.Id `json:"provider-id" yaml:"provider-id"` 204 CIDR string `json:"cidr,omitempty" yaml:"cidr,omitempty"` 205 VLANTag int `json:"vlan-tag,omitempty" yaml:"vlan-tag,omitempty"` 206 } 207 208 type networkStatusNoMarshal networkStatus 209 210 func (n networkStatus) MarshalJSON() ([]byte, error) { 211 if n.Err != nil { 212 return json.Marshal(errorStatus{n.Err.Error()}) 213 } 214 type nNoMethods networkStatus 215 return json.Marshal(nNoMethods(n)) 216 } 217 218 func (n networkStatus) GetYAML() (tag string, value interface{}) { 219 if n.Err != nil { 220 return "", errorStatus{n.Err.Error()} 221 } 222 type nNoMethods networkStatus 223 return "", nNoMethods(n) 224 } 225 226 func formatStatus(status *api.Status) formattedStatus { 227 if status == nil { 228 return formattedStatus{} 229 } 230 out := formattedStatus{ 231 Environment: status.EnvironmentName, 232 Machines: make(map[string]machineStatus), 233 Services: make(map[string]serviceStatus), 234 } 235 for k, m := range status.Machines { 236 out.Machines[k] = formatMachine(m) 237 } 238 for k, s := range status.Services { 239 out.Services[k] = formatService(s) 240 } 241 for k, n := range status.Networks { 242 if out.Networks == nil { 243 out.Networks = make(map[string]networkStatus) 244 } 245 out.Networks[k] = formatNetwork(n) 246 } 247 return out 248 } 249 250 func formatMachine(machine api.MachineStatus) machineStatus { 251 out := machineStatus{ 252 Err: machine.Err, 253 AgentState: machine.AgentState, 254 AgentStateInfo: machine.AgentStateInfo, 255 AgentVersion: machine.AgentVersion, 256 DNSName: machine.DNSName, 257 InstanceId: machine.InstanceId, 258 InstanceState: machine.InstanceState, 259 Life: machine.Life, 260 Series: machine.Series, 261 Id: machine.Id, 262 Containers: make(map[string]machineStatus), 263 Hardware: machine.Hardware, 264 } 265 for k, m := range machine.Containers { 266 out.Containers[k] = formatMachine(m) 267 } 268 269 for _, job := range machine.Jobs { 270 if job == params.JobManageEnviron { 271 out.HAStatus = makeHAStatus(machine.HasVote, machine.WantsVote) 272 break 273 } 274 } 275 return out 276 } 277 278 func makeHAStatus(hasVote, wantsVote bool) string { 279 var s string 280 switch { 281 case hasVote && wantsVote: 282 s = "has-vote" 283 case hasVote && !wantsVote: 284 s = "removing-vote" 285 case !hasVote && wantsVote: 286 s = "adding-vote" 287 case !hasVote && !wantsVote: 288 s = "no-vote" 289 } 290 return s 291 } 292 293 func formatService(service api.ServiceStatus) serviceStatus { 294 out := serviceStatus{ 295 Err: service.Err, 296 Charm: service.Charm, 297 Exposed: service.Exposed, 298 Life: service.Life, 299 Relations: service.Relations, 300 Networks: make(map[string][]string), 301 CanUpgradeTo: service.CanUpgradeTo, 302 SubordinateTo: service.SubordinateTo, 303 Units: make(map[string]unitStatus), 304 } 305 if len(service.Networks.Enabled) > 0 { 306 out.Networks["enabled"] = service.Networks.Enabled 307 } 308 if len(service.Networks.Disabled) > 0 { 309 out.Networks["disabled"] = service.Networks.Disabled 310 } 311 for k, m := range service.Units { 312 out.Units[k] = formatUnit(m) 313 } 314 return out 315 } 316 317 func formatUnit(unit api.UnitStatus) unitStatus { 318 out := unitStatus{ 319 Err: unit.Err, 320 AgentState: unit.AgentState, 321 AgentStateInfo: unit.AgentStateInfo, 322 AgentVersion: unit.AgentVersion, 323 Life: unit.Life, 324 Machine: unit.Machine, 325 OpenedPorts: unit.OpenedPorts, 326 PublicAddress: unit.PublicAddress, 327 Charm: unit.Charm, 328 Subordinates: make(map[string]unitStatus), 329 } 330 for k, m := range unit.Subordinates { 331 out.Subordinates[k] = formatUnit(m) 332 } 333 return out 334 } 335 336 func formatNetwork(network api.NetworkStatus) networkStatus { 337 return networkStatus{ 338 Err: network.Err, 339 ProviderId: network.ProviderId, 340 CIDR: network.CIDR, 341 VLANTag: network.VLANTag, 342 } 343 }