github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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 "launchpad.net/juju-core/cmd" 13 "launchpad.net/juju-core/instance" 14 "launchpad.net/juju-core/juju" 15 "launchpad.net/juju-core/state/api" 16 "launchpad.net/juju-core/state/api/params" 17 "launchpad.net/juju-core/state/statecmd" 18 ) 19 20 type StatusCommand struct { 21 cmd.EnvCommandBase 22 out cmd.Output 23 patterns []string 24 } 25 26 var statusDoc = ` 27 This command will report on the runtime state of various system entities. 28 29 Service or unit names may be specified to filter the status to only those 30 services and units that match, along with the related machines, services 31 and units. If a subordinate unit is matched, then its principal unit will 32 be displayed. If a principal unit is matched, then all of its subordinates 33 will be displayed. 34 35 Wildcards ('*') may be specified in service/unit names to match any sequence 36 of characters. For example, 'nova-*' will match any service whose name begins 37 with 'nova-': 'nova-compute', 'nova-volume', etc. 38 ` 39 40 func (c *StatusCommand) Info() *cmd.Info { 41 return &cmd.Info{ 42 Name: "status", 43 Args: "[pattern ...]", 44 Purpose: "output status information about an environment", 45 Doc: statusDoc, 46 Aliases: []string{"stat"}, 47 } 48 } 49 50 func (c *StatusCommand) SetFlags(f *gnuflag.FlagSet) { 51 c.EnvCommandBase.SetFlags(f) 52 c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{ 53 "yaml": cmd.FormatYaml, 54 "json": cmd.FormatJson, 55 }) 56 } 57 58 func (c *StatusCommand) Init(args []string) error { 59 c.patterns = args 60 return nil 61 } 62 63 var connectionError = `Unable to connect to environment "%s". 64 Please check your credentials or use 'juju bootstrap' to create a new environment. 65 66 Error details: 67 %v 68 ` 69 70 func (c *StatusCommand) getStatus1dot16() (*api.Status, error) { 71 conn, err := juju.NewConnFromName(c.EnvName) 72 if err != nil { 73 return nil, fmt.Errorf(connectionError, c.EnvName, err) 74 } 75 defer conn.Close() 76 77 return statecmd.Status(conn, c.patterns) 78 } 79 80 func (c *StatusCommand) Run(ctx *cmd.Context) error { 81 // Just verify the pattern validity client side, do not use the matcher 82 _, err := statecmd.NewUnitMatcher(c.patterns) 83 if err != nil { 84 return err 85 } 86 apiclient, err := juju.NewAPIClientFromName(c.EnvName) 87 if err != nil { 88 return fmt.Errorf(connectionError, c.EnvName, err) 89 } 90 defer apiclient.Close() 91 92 status, err := apiclient.Status(c.patterns) 93 if params.IsCodeNotImplemented(err) { 94 logger.Infof("Status not supported by the API server, " + 95 "falling back to 1.16 compatibility mode " + 96 "(direct DB access)") 97 status, err = c.getStatus1dot16() 98 } 99 // Display any error, but continue to print status if some was returned 100 if err != nil { 101 fmt.Fprintf(ctx.Stderr, "%v\n", err) 102 } 103 result := formatStatus(status) 104 return c.out.Write(ctx, result) 105 } 106 107 type formattedStatus struct { 108 Environment string `json:"environment"` 109 Machines map[string]machineStatus `json:"machines"` 110 Services map[string]serviceStatus `json:"services"` 111 } 112 113 type errorStatus struct { 114 StatusError string `json:"status-error" yaml:"status-error"` 115 } 116 117 type machineStatus struct { 118 Err error `json:"-" yaml:",omitempty"` 119 AgentState params.Status `json:"agent-state,omitempty" yaml:"agent-state,omitempty"` 120 AgentStateInfo string `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"` 121 AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"` 122 DNSName string `json:"dns-name,omitempty" yaml:"dns-name,omitempty"` 123 InstanceId instance.Id `json:"instance-id,omitempty" yaml:"instance-id,omitempty"` 124 InstanceState string `json:"instance-state,omitempty" yaml:"instance-state,omitempty"` 125 Life string `json:"life,omitempty" yaml:"life,omitempty"` 126 Series string `json:"series,omitempty" yaml:"series,omitempty"` 127 Id string `json:"-" yaml:"-"` 128 Containers map[string]machineStatus `json:"containers,omitempty" yaml:"containers,omitempty"` 129 Hardware string `json:"hardware,omitempty" yaml:"hardware,omitempty"` 130 } 131 132 // A goyaml bug means we can't declare these types 133 // locally to the GetYAML methods. 134 type machineStatusNoMarshal machineStatus 135 136 func (s machineStatus) MarshalJSON() ([]byte, error) { 137 if s.Err != nil { 138 return json.Marshal(errorStatus{s.Err.Error()}) 139 } 140 return json.Marshal(machineStatusNoMarshal(s)) 141 } 142 143 func (s machineStatus) GetYAML() (tag string, value interface{}) { 144 if s.Err != nil { 145 return "", errorStatus{s.Err.Error()} 146 } 147 // TODO(rog) rename mNoMethods to noMethods (and also in 148 // the other GetYAML methods) when people are using the non-buggy 149 // goyaml version. 150 type mNoMethods machineStatus 151 return "", mNoMethods(s) 152 } 153 154 type serviceStatus struct { 155 Err error `json:"-" yaml:",omitempty"` 156 Charm string `json:"charm" yaml:"charm"` 157 CanUpgradeTo string `json:"can-upgrade-to,omitempty" yaml:"can-upgrade-to,omitempty"` 158 Exposed bool `json:"exposed" yaml:"exposed"` 159 Life string `json:"life,omitempty" yaml:"life,omitempty"` 160 Relations map[string][]string `json:"relations,omitempty" yaml:"relations,omitempty"` 161 SubordinateTo []string `json:"subordinate-to,omitempty" yaml:"subordinate-to,omitempty"` 162 Units map[string]unitStatus `json:"units,omitempty" yaml:"units,omitempty"` 163 } 164 165 type serviceStatusNoMarshal serviceStatus 166 167 func (s serviceStatus) MarshalJSON() ([]byte, error) { 168 if s.Err != nil { 169 return json.Marshal(errorStatus{s.Err.Error()}) 170 } 171 type sNoMethods serviceStatus 172 return json.Marshal(sNoMethods(s)) 173 } 174 175 func (s serviceStatus) GetYAML() (tag string, value interface{}) { 176 if s.Err != nil { 177 return "", errorStatus{s.Err.Error()} 178 } 179 type sNoMethods serviceStatus 180 return "", sNoMethods(s) 181 } 182 183 type unitStatus struct { 184 Err error `json:"-" yaml:",omitempty"` 185 Charm string `json:"upgrading-from,omitempty" yaml:"upgrading-from,omitempty"` 186 AgentState params.Status `json:"agent-state,omitempty" yaml:"agent-state,omitempty"` 187 AgentStateInfo string `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"` 188 AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"` 189 Life string `json:"life,omitempty" yaml:"life,omitempty"` 190 Machine string `json:"machine,omitempty" yaml:"machine,omitempty"` 191 OpenedPorts []string `json:"open-ports,omitempty" yaml:"open-ports,omitempty"` 192 PublicAddress string `json:"public-address,omitempty" yaml:"public-address,omitempty"` 193 Subordinates map[string]unitStatus `json:"subordinates,omitempty" yaml:"subordinates,omitempty"` 194 } 195 196 type unitStatusNoMarshal unitStatus 197 198 func (s unitStatus) MarshalJSON() ([]byte, error) { 199 if s.Err != nil { 200 return json.Marshal(errorStatus{s.Err.Error()}) 201 } 202 return json.Marshal(unitStatusNoMarshal(s)) 203 } 204 205 func (s unitStatus) GetYAML() (tag string, value interface{}) { 206 if s.Err != nil { 207 return "", errorStatus{s.Err.Error()} 208 } 209 type uNoMethods unitStatus 210 return "", unitStatusNoMarshal(s) 211 } 212 213 func formatStatus(status *api.Status) formattedStatus { 214 if status == nil { 215 return formattedStatus{} 216 } 217 out := formattedStatus{ 218 Environment: status.EnvironmentName, 219 Machines: make(map[string]machineStatus), 220 Services: make(map[string]serviceStatus), 221 } 222 for k, m := range status.Machines { 223 out.Machines[k] = formatMachine(m) 224 } 225 for k, s := range status.Services { 226 out.Services[k] = formatService(s) 227 } 228 return out 229 } 230 231 func formatMachine(machine api.MachineStatus) machineStatus { 232 out := machineStatus{ 233 Err: machine.Err, 234 AgentState: machine.AgentState, 235 AgentStateInfo: machine.AgentStateInfo, 236 AgentVersion: machine.AgentVersion, 237 DNSName: machine.DNSName, 238 InstanceId: machine.InstanceId, 239 InstanceState: machine.InstanceState, 240 Life: machine.Life, 241 Series: machine.Series, 242 Id: machine.Id, 243 Containers: make(map[string]machineStatus), 244 Hardware: machine.Hardware, 245 } 246 for k, m := range machine.Containers { 247 out.Containers[k] = formatMachine(m) 248 } 249 return out 250 } 251 252 func formatService(service api.ServiceStatus) serviceStatus { 253 out := serviceStatus{ 254 Err: service.Err, 255 Charm: service.Charm, 256 Exposed: service.Exposed, 257 Life: service.Life, 258 Relations: service.Relations, 259 CanUpgradeTo: service.CanUpgradeTo, 260 SubordinateTo: service.SubordinateTo, 261 Units: make(map[string]unitStatus), 262 } 263 for k, m := range service.Units { 264 out.Units[k] = formatUnit(m) 265 } 266 return out 267 } 268 269 func formatUnit(unit api.UnitStatus) unitStatus { 270 out := unitStatus{ 271 Err: unit.Err, 272 AgentState: unit.AgentState, 273 AgentStateInfo: unit.AgentStateInfo, 274 AgentVersion: unit.AgentVersion, 275 Life: unit.Life, 276 Machine: unit.Machine, 277 OpenedPorts: unit.OpenedPorts, 278 PublicAddress: unit.PublicAddress, 279 Charm: unit.Charm, 280 Subordinates: make(map[string]unitStatus), 281 } 282 for k, m := range unit.Subordinates { 283 out.Subordinates[k] = formatUnit(m) 284 } 285 return out 286 }