github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/status/formatter.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package status 5 6 import ( 7 "github.com/juju/juju/apiserver/params" 8 "github.com/juju/juju/cmd/juju/common" 9 "github.com/juju/juju/state/multiwatcher" 10 "github.com/juju/juju/status" 11 ) 12 13 type statusFormatter struct { 14 status *params.FullStatus 15 relations map[int]params.RelationStatus 16 isoTime bool 17 } 18 19 // NewStatusFormatter takes stored model information (params.FullStatus) and populates 20 // the statusFormatter struct used in various status formatting methods 21 func NewStatusFormatter(status *params.FullStatus, isoTime bool) *statusFormatter { 22 sf := statusFormatter{ 23 status: status, 24 relations: make(map[int]params.RelationStatus), 25 isoTime: isoTime, 26 } 27 for _, relation := range status.Relations { 28 sf.relations[relation.Id] = relation 29 } 30 return &sf 31 } 32 33 func (sf *statusFormatter) format() formattedStatus { 34 if sf.status == nil { 35 return formattedStatus{} 36 } 37 out := formattedStatus{ 38 Model: sf.status.ModelName, 39 Machines: make(map[string]machineStatus), 40 Services: make(map[string]serviceStatus), 41 } 42 if sf.status.AvailableVersion != "" { 43 out.ModelStatus = &modelStatus{ 44 AvailableVersion: sf.status.AvailableVersion, 45 } 46 } 47 48 for k, m := range sf.status.Machines { 49 out.Machines[k] = sf.formatMachine(m) 50 } 51 for sn, s := range sf.status.Services { 52 out.Services[sn] = sf.formatService(sn, s) 53 } 54 return out 55 } 56 57 // MachineFormat takes stored model information (params.FullStatus) and formats machine status info. 58 func (sf *statusFormatter) MachineFormat(machineId []string) formattedMachineStatus { 59 if sf.status == nil { 60 return formattedMachineStatus{} 61 } 62 out := formattedMachineStatus{ 63 Model: sf.status.ModelName, 64 Machines: make(map[string]machineStatus), 65 } 66 for k, m := range sf.status.Machines { 67 if len(machineId) != 0 { 68 for i := 0; i < len(machineId); i++ { 69 if m.Id == machineId[i] { 70 out.Machines[k] = sf.formatMachine(m) 71 } 72 } 73 } else { 74 out.Machines[k] = sf.formatMachine(m) 75 } 76 } 77 return out 78 } 79 80 func (sf *statusFormatter) formatMachine(machine params.MachineStatus) machineStatus { 81 var out machineStatus 82 83 out = machineStatus{ 84 JujuStatus: sf.getStatusInfoContents(machine.AgentStatus), 85 DNSName: machine.DNSName, 86 InstanceId: machine.InstanceId, 87 MachineStatus: sf.getStatusInfoContents(machine.InstanceStatus), 88 Series: machine.Series, 89 Id: machine.Id, 90 Containers: make(map[string]machineStatus), 91 Hardware: machine.Hardware, 92 } 93 94 for k, m := range machine.Containers { 95 out.Containers[k] = sf.formatMachine(m) 96 } 97 98 for _, job := range machine.Jobs { 99 if job == multiwatcher.JobManageModel { 100 out.HAStatus = makeHAStatus(machine.HasVote, machine.WantsVote) 101 break 102 } 103 } 104 return out 105 } 106 107 func (sf *statusFormatter) formatService(name string, service params.ServiceStatus) serviceStatus { 108 out := serviceStatus{ 109 Err: service.Err, 110 Charm: service.Charm, 111 Exposed: service.Exposed, 112 Life: service.Life, 113 Relations: service.Relations, 114 CanUpgradeTo: service.CanUpgradeTo, 115 SubordinateTo: service.SubordinateTo, 116 Units: make(map[string]unitStatus), 117 StatusInfo: sf.getServiceStatusInfo(service), 118 } 119 for k, m := range service.Units { 120 out.Units[k] = sf.formatUnit(unitFormatInfo{ 121 unit: m, 122 unitName: k, 123 serviceName: name, 124 meterStatuses: service.MeterStatuses, 125 }) 126 } 127 return out 128 } 129 130 func (sf *statusFormatter) getServiceStatusInfo(service params.ServiceStatus) statusInfoContents { 131 info := statusInfoContents{ 132 Err: service.Status.Err, 133 Current: service.Status.Status, 134 Message: service.Status.Info, 135 Version: service.Status.Version, 136 } 137 if service.Status.Since != nil { 138 info.Since = common.FormatTime(service.Status.Since, sf.isoTime) 139 } 140 return info 141 } 142 143 type unitFormatInfo struct { 144 unit params.UnitStatus 145 unitName string 146 serviceName string 147 meterStatuses map[string]params.MeterStatus 148 } 149 150 func (sf *statusFormatter) formatUnit(info unitFormatInfo) unitStatus { 151 // TODO(Wallyworld) - this should be server side but we still need to support older servers. 152 sf.updateUnitStatusInfo(&info.unit, info.serviceName) 153 154 out := unitStatus{ 155 WorkloadStatusInfo: sf.getWorkloadStatusInfo(info.unit), 156 JujuStatusInfo: sf.getAgentStatusInfo(info.unit), 157 Machine: info.unit.Machine, 158 OpenedPorts: info.unit.OpenedPorts, 159 PublicAddress: info.unit.PublicAddress, 160 Charm: info.unit.Charm, 161 Subordinates: make(map[string]unitStatus), 162 } 163 164 if ms, ok := info.meterStatuses[info.unitName]; ok { 165 out.MeterStatus = &meterStatus{ 166 Color: ms.Color, 167 Message: ms.Message, 168 } 169 } 170 171 for k, m := range info.unit.Subordinates { 172 out.Subordinates[k] = sf.formatUnit(unitFormatInfo{ 173 unit: m, 174 unitName: k, 175 serviceName: info.serviceName, 176 meterStatuses: info.meterStatuses, 177 }) 178 } 179 return out 180 } 181 182 func (sf *statusFormatter) getStatusInfoContents(inst params.DetailedStatus) statusInfoContents { 183 info := statusInfoContents{ 184 Err: inst.Err, 185 Current: inst.Status, 186 Message: inst.Info, 187 Version: inst.Version, 188 Life: inst.Life, 189 } 190 if inst.Since != nil { 191 info.Since = common.FormatTime(inst.Since, sf.isoTime) 192 } 193 return info 194 } 195 196 func (sf *statusFormatter) getWorkloadStatusInfo(unit params.UnitStatus) statusInfoContents { 197 info := statusInfoContents{ 198 Err: unit.WorkloadStatus.Err, 199 Current: unit.WorkloadStatus.Status, 200 Message: unit.WorkloadStatus.Info, 201 Version: unit.WorkloadStatus.Version, 202 } 203 if unit.WorkloadStatus.Since != nil { 204 info.Since = common.FormatTime(unit.WorkloadStatus.Since, sf.isoTime) 205 } 206 return info 207 } 208 209 func (sf *statusFormatter) getAgentStatusInfo(unit params.UnitStatus) statusInfoContents { 210 info := statusInfoContents{ 211 Err: unit.AgentStatus.Err, 212 Current: unit.AgentStatus.Status, 213 Message: unit.AgentStatus.Info, 214 Version: unit.AgentStatus.Version, 215 } 216 if unit.AgentStatus.Since != nil { 217 info.Since = common.FormatTime(unit.AgentStatus.Since, sf.isoTime) 218 } 219 return info 220 } 221 222 func (sf *statusFormatter) updateUnitStatusInfo(unit *params.UnitStatus, serviceName string) { 223 if unit.WorkloadStatus.Status == status.StatusError { 224 if relation, ok := sf.relations[getRelationIdFromData(unit)]; ok { 225 // Append the details of the other endpoint on to the status info string. 226 if ep, ok := findOtherEndpoint(relation.Endpoints, serviceName); ok { 227 unit.WorkloadStatus.Info = unit.WorkloadStatus.Info + " for " + ep.String() 228 } 229 } 230 } 231 } 232 233 func makeHAStatus(hasVote, wantsVote bool) string { 234 var s string 235 switch { 236 case hasVote && wantsVote: 237 s = "has-vote" 238 case hasVote && !wantsVote: 239 s = "removing-vote" 240 case !hasVote && wantsVote: 241 s = "adding-vote" 242 case !hasVote && !wantsVote: 243 s = "no-vote" 244 } 245 return s 246 } 247 248 func getRelationIdFromData(unit *params.UnitStatus) int { 249 if relationId_, ok := unit.WorkloadStatus.Data["relation-id"]; ok { 250 if relationId, ok := relationId_.(float64); ok { 251 return int(relationId) 252 } else { 253 logger.Infof("relation-id found status data but was unexpected "+ 254 "type: %q. Status output may be lacking some detail.", relationId_) 255 } 256 } 257 return -1 258 } 259 260 // findOtherEndpoint searches the provided endpoints for an endpoint 261 // that *doesn't* match serviceName. The returned bool indicates if 262 // such an endpoint was found. 263 func findOtherEndpoint(endpoints []params.EndpointStatus, serviceName string) (params.EndpointStatus, bool) { 264 for _, endpoint := range endpoints { 265 if endpoint.ServiceName != serviceName { 266 return endpoint, true 267 } 268 } 269 return params.EndpointStatus{}, false 270 }