Permalink
Dec 20, 2013
Dec 20, 2013
Dec 20, 2013
Dec 20, 2013
Dec 20, 2013
Apr 16, 2014
Apr 16, 2014
Apr 16, 2014
Apr 17, 2014
Apr 16, 2014
Sep 28, 2015
Apr 16, 2014
Apr 16, 2014
Apr 17, 2014
Sep 28, 2015
Apr 16, 2014
Apr 16, 2014
Apr 16, 2014
Apr 16, 2014
Newer
100644
1117 lines (1012 sloc)
34.2 KB
1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
3
4
package client
5
6
import (
24
)
25
26
func agentStatusFromStatusInfo(s []status.StatusInfo, kind status.HistoryKind) []params.DetailedStatus {
27
result := []params.DetailedStatus{}
53
// unitStatusHistory returns a list of status history entries for unit agents or workloads.
54
func (c *Client) unitStatusHistory(unitTag names.UnitTag, filter status.StatusHistoryFilter, kind status.HistoryKind) ([]params.DetailedStatus, error) {
55
unit, err := c.api.stateAccessor.Unit(unitTag.Id())
59
statuses := []params.DetailedStatus{}
60
if kind == status.KindUnit || kind == status.KindWorkload {
61
unitStatuses, err := unit.StatusHistory(filter)
68
if kind == status.KindUnit || kind == status.KindUnitAgent {
69
agentStatuses, err := unit.AgentHistory().StatusHistory(filter)
71
return nil, errors.Trace(err)
72
}
73
statuses = append(statuses, agentStatusFromStatusInfo(agentStatuses, status.KindUnitAgent)...)
74
}
75
76
sort.Sort(byTime(statuses))
77
if kind == status.KindUnit && filter.Size > 0 {
78
if len(statuses) > filter.Size {
79
statuses = statuses[len(statuses)-filter.Size:]
83
return statuses, nil
84
}
85
86
// machineStatusHistory returns status history for the given machine.
87
func (c *Client) machineStatusHistory(machineTag names.MachineTag, filter status.StatusHistoryFilter, kind status.HistoryKind) ([]params.DetailedStatus, error) {
88
machine, err := c.api.stateAccessor.Machine(machineTag.Id())
89
if err != nil {
90
return nil, errors.Trace(err)
91
}
92
var sInfo []status.StatusInfo
93
if kind == status.KindMachineInstance || kind == status.KindContainerInstance {
94
sInfo, err = machine.InstanceStatusHistory(filter)
95
} else {
96
sInfo, err = machine.StatusHistory(filter)
97
}
98
if err != nil {
99
return nil, errors.Trace(err)
100
}
101
return agentStatusFromStatusInfo(sInfo, kind), nil
102
}
103
104
// StatusHistory returns a slice of past statuses for several entities.
105
func (c *Client) StatusHistory(request params.StatusHistoryRequests) params.StatusHistoryResults {
106
107
results := params.StatusHistoryResults{}
108
// TODO(perrito666) the contents of the loop could be split into
109
// a oneHistory method for clarity.
110
for _, request := range request.Requests {
111
filter := status.StatusHistoryFilter{
112
Size: request.Filter.Size,
113
FromDate: request.Filter.Date,
114
Delta: request.Filter.Delta,
115
Exclude: set.NewStrings(request.Filter.Exclude...),
116
}
117
if err := c.checkCanRead(); err != nil {
118
history := params.StatusHistoryResult{
119
Error: common.ServerError(err),
120
}
121
results.Results = append(results.Results, history)
122
continue
126
if err := filter.Validate(); err != nil {
127
history := params.StatusHistoryResult{
128
Error: common.ServerError(errors.Annotate(err, "cannot validate status history filter")),
129
}
130
results.Results = append(results.Results, history)
131
continue
132
}
133
134
var (
135
err error
136
hist []params.DetailedStatus
137
)
138
kind := status.HistoryKind(request.Kind)
139
err = errors.NotValidf("%q requires a unit, got %T", kind, request.Tag)
140
switch kind {
141
case status.KindUnit, status.KindWorkload, status.KindUnitAgent:
142
var u names.UnitTag
143
if u, err = names.ParseUnitTag(request.Tag); err == nil {
144
hist, err = c.unitStatusHistory(u, filter, kind)
145
}
146
default:
147
var m names.MachineTag
148
if m, err = names.ParseMachineTag(request.Tag); err == nil {
149
hist, err = c.machineStatusHistory(m, filter, kind)
150
}
151
}
152
153
if err == nil {
154
sort.Sort(byTime(hist))
155
}
156
157
results.Results = append(results.Results,
158
params.StatusHistoryResult{
159
History: params.History{Statuses: hist},
160
Error: common.ServerError(errors.Annotatef(err, "fetching status history for %q", request.Tag)),
161
})
170
}
174
var err error
175
if context.applications, context.units, context.latestCharms, err =
176
fetchAllApplicationsAndUnits(c.api.stateAccessor, len(args.Patterns) <= 0); err != nil {
177
return noStatus, errors.Annotate(err, "could not fetch applications and units")
178
}
179
if featureflag.Enabled(feature.CrossModelRelations) {
180
if context.remoteApplications, err =
181
fetchRemoteApplications(c.api.stateAccessor); err != nil {
182
return noStatus, errors.Annotate(err, "could not fetch remote applications")
183
}
184
}
185
if context.machines, err = fetchMachines(c.api.stateAccessor, nil); err != nil {
188
// These may be empty when machines have not finished deployment.
189
if context.ipAddresses, context.spaces, context.linkLayerDevices, err =
190
fetchNetworkInterfaces(c.api.stateAccessor); err != nil {
191
return noStatus, errors.Annotate(err, "could not fetch IP addresses and link layer devices")
192
}
195
}
196
if len(context.applications) > 0 {
197
if context.leaders, err = c.api.stateAccessor.ApplicationLeaders(); err != nil {
198
return noStatus, errors.Annotate(err, " could not fetch leaders")
199
}
202
logger.Debugf("Applications: %v", context.applications)
203
logger.Debugf("Remote applications: %v", context.remoteApplications)
208
// First, attempt to match machines. Any units on those
209
// machines are implicitly matched.
210
matchedMachines := make(set.Strings)
211
for _, machineList := range context.machines {
212
for _, m := range machineList {
213
matches, err := predicate(m)
214
if err != nil {
215
return noStatus, errors.Annotate(
216
err, "could not filter machines",
217
)
218
}
219
if matches {
220
matchedMachines.Add(m.Id())
221
}
222
}
223
}
224
230
machineId, err := unit.AssignedMachineId()
231
if err != nil {
232
machineId = ""
233
} else if matchedMachines.Contains(machineId) {
234
// Unit is on a matching machine.
235
matchedSvcs.Add(unit.ApplicationName())
236
continue
237
}
238
239
// Always start examining at the top-level. This
240
// prevents a situation where we filter a subordinate
250
matchedSvcs.Add(unit.ApplicationName())
251
if machineId != "" {
252
matchedMachines.Add(machineId)
257
// Filter applications
258
for svcName, svc := range context.applications {
259
if matchedSvcs.Contains(svcName) {
260
// There are matched units for this application.
275
machineContainers, err := m.Containers()
276
if err != nil {
277
return noStatus, err
278
}
279
machineContainersSet := set.NewStrings(machineContainers...)
280
281
if matchedMachines.Contains(m.Id()) || !matchedMachines.Intersection(machineContainersSet).IsEmpty() {
282
// The machine is matched directly, or contains a unit
283
// or container that matches.
284
logger.Tracef("machine %s is hosting something.", m.Id())
285
matched = append(matched, m)
292
298
Model: modelStatus,
299
Machines: processMachines(
300
context.machines,
301
context.ipAddresses,
302
context.spaces,
303
context.linkLayerDevices,
304
),
305
Applications: context.processApplications(),
306
RemoteApplications: context.processRemoteApplications(),
307
Relations: context.processRelations(),
309
}
311
// newToolsVersionAvailable will return a string representing a tools
312
// version only if the latest check is newer than current tools.
313
func (c *Client) modelStatus() (params.ModelStatusInfo, error) {
314
var info params.ModelStatusInfo
315
316
m, err := c.api.stateAccessor.Model()
320
info.Name = m.Name()
321
info.CloudTag = names.NewCloudTag(m.Cloud()).String()
322
info.CloudRegion = m.CloudRegion()
326
return params.ModelStatusInfo{}, errors.Annotate(err, "cannot obtain current model config")
328
329
latestVersion := m.LatestToolsVersion()
330
current, ok := cfg.AgentVersion()
331
if ok {
332
info.Version = current.String()
333
if current.Compare(latestVersion) < 0 {
334
info.AvailableVersion = latestVersion.String()
335
}
340
return params.ModelStatusInfo{}, errors.Annotate(err, "cannot obtain model status info")
343
info.ModelStatus = params.DetailedStatus{
344
Status: status.Status.String(),
345
Info: status.Message,
346
Since: status.Since,
347
Data: status.Data,
354
// machines: top-level machine id -> list of machines nested in
355
// this machine.
356
machines map[string][]*state.Machine
357
358
// ipAddresses: machine id -> list of ip.addresses
359
ipAddresses map[string][]*state.Address
360
361
// spaces: machine id -> deviceName -> list of spaceNames
362
spaces map[string]map[string]set.Strings
363
364
// linkLayerDevices: machine id -> list of linkLayerDevices
365
linkLayerDevices map[string][]*state.LinkLayerDevice
366
367
// applications: application name -> application
368
applications map[string]*state.Application
370
// remote applications: application name -> application
371
remoteApplications map[string]*state.RemoteApplication
372
relations map[string][]*state.Relation
373
units map[string]map[string]*state.Unit
374
latestCharms map[charm.URL]*state.Charm
375
leaders map[string]string
376
}
377
378
// fetchMachines returns a map from top level machine id to machines, where machines[0] is the host
379
// machine and machines[1..n] are any containers (including nested ones).
380
//
381
// If machineIds is non-nil, only machines whose IDs are in the set are returned.
382
func fetchMachines(st Backend, machineIds set.Strings) (map[string][]*state.Machine, error) {
383
v := make(map[string][]*state.Machine)
384
machines, err := st.AllMachines()
385
if err != nil {
386
return nil, err
387
}
388
// AllMachines gives us machines sorted by id.
389
for _, m := range machines {
390
if machineIds != nil && !machineIds.Contains(m.Id()) {
391
continue
392
}
393
parentId, ok := m.ParentId()
394
if !ok {
395
// Only top level host machines go directly into the machine map.
396
v[m.Id()] = []*state.Machine{m}
397
} else {
398
topParentId := state.TopParentId(m.Id())
399
machines, ok := v[topParentId]
400
if !ok {
401
panic(fmt.Errorf("unexpected machine id %q", parentId))
402
}
403
machines = append(machines, m)
404
v[topParentId] = machines
405
}
406
}
407
return v, nil
408
}
409
412
// linklayerdevices.
413
//
414
// All are required to determine a machine's network interfaces configuration,
415
// so we want all or none.
416
func fetchNetworkInterfaces(st Backend) (map[string][]*state.Address, map[string]map[string]set.Strings, map[string][]*state.LinkLayerDevice, error) {
417
ipAddresses := make(map[string][]*state.Address)
418
spaces := make(map[string]map[string]set.Strings)
419
ipAddrs, err := st.AllIPAddresses()
420
if err != nil {
421
return nil, nil, nil, err
422
}
423
for _, ipAddr := range ipAddrs {
424
if ipAddr.LoopbackConfigMethod() {
425
continue
426
}
427
machineID := ipAddr.MachineID()
428
ipAddresses[machineID] = append(ipAddresses[machineID], ipAddr)
429
subnet, err := st.Subnet(ipAddr.SubnetCIDR())
430
if errors.IsNotFound(err) {
431
// No worries; no subnets means no spaces.
432
continue
433
} else if err != nil {
434
return nil, nil, nil, err
435
}
436
if spaceName := subnet.SpaceName(); spaceName != "" {
437
devices, ok := spaces[machineID]
438
if !ok {
439
devices = make(map[string]set.Strings)
440
spaces[machineID] = devices
442
deviceName := ipAddr.DeviceName()
443
spacesSet, ok := devices[deviceName]
444
if !ok {
445
spacesSet = make(set.Strings)
446
devices[deviceName] = spacesSet
449
}
450
}
451
452
linkLayerDevices := make(map[string][]*state.LinkLayerDevice)
453
llDevs, err := st.AllLinkLayerDevices()
454
if err != nil {
455
return nil, nil, nil, err
456
}
457
for _, llDev := range llDevs {
458
if llDev.IsLoopbackDevice() {
459
continue
460
}
461
addrs, err := llDev.Addresses()
462
if err != nil {
463
return nil, nil, nil, err
464
}
465
// We don't want to see bond slaves or bridge ports, only the
466
// IP-addressed devices.
467
if len(addrs) > 0 {
468
machineID := llDev.MachineID()
469
linkLayerDevices[machineID] = append(linkLayerDevices[machineID], llDev)
470
}
471
}
472
473
return ipAddresses, spaces, linkLayerDevices, nil
474
}
475
476
// fetchAllApplicationsAndUnits returns a map from application name to application,
477
// a map from application name to unit name to unit, and a map from base charm URL to latest URL.
478
func fetchAllApplicationsAndUnits(
479
st Backend,
481
) (map[string]*state.Application, map[string]map[string]*state.Unit, map[charm.URL]*state.Charm, error) {
485
latestCharms := make(map[charm.URL]*state.Charm)
486
applications, err := st.AllApplications()
499
if matchAny || len(appUnitMap) > 0 {
500
unitMap[s.Name()] = appUnitMap
501
appMap[s.Name()] = s
502
// Record the base URL for the application's charm so that
503
// the latest store revision can be looked up.
504
charmURL, _ := s.CharmURL()
505
if charmURL.Schema == "cs" {
524
// fetchRemoteApplications returns a map from application name to remote application.
525
func fetchRemoteApplications(st Backend) (map[string]*state.RemoteApplication, error) {
526
appMap := make(map[string]*state.RemoteApplication)
527
applications, err := st.AllRemoteApplications()
531
for _, a := range applications {
532
if _, ok := a.URL(); !ok {
533
continue
534
}
535
appMap[a.Name()] = a
542
// This structure is useful for processApplicationRelations() which needs
543
// to have the relations for each application. Reading them once here
545
// application that used to happen in processApplicationRelations().
546
func fetchRelations(st Backend) (map[string][]*state.Relation, error) {
547
relations, err := st.AllRelations()
548
if err != nil {
549
return nil, err
550
}
551
out := make(map[string][]*state.Relation)
552
for _, relation := range relations {
553
for _, ep := range relation.Endpoints() {
560
type machineAndContainers map[string][]*state.Machine
561
562
func (m machineAndContainers) HostForMachineId(id string) *state.Machine {
563
// Element 0 is assumed to be the top-level machine.
564
return m[id][0]
565
}
566
567
func (m machineAndContainers) Containers(id string) []*state.Machine {
568
return m[id][1:]
569
}
570
571
func processMachines(
572
idToMachines map[string][]*state.Machine,
573
idToIpAddresses map[string][]*state.Address,
574
idToDeviceToSpaces map[string]map[string]set.Strings,
575
idToLinkLayerDevices map[string][]*state.LinkLayerDevice,
576
) map[string]params.MachineStatus {
577
machinesMap := make(map[string]params.MachineStatus)
578
cache := make(map[string]params.MachineStatus)
579
for id, machines := range idToMachines {
580
581
if len(machines) <= 0 {
582
continue
587
hostStatus := makeMachineStatus(
588
tlMachine,
589
idToIpAddresses[tlMachine.Id()],
590
idToDeviceToSpaces[tlMachine.Id()],
591
idToLinkLayerDevices[tlMachine.Id()],
592
)
593
machinesMap[id] = hostStatus
594
cache[id] = hostStatus
595
596
for _, machine := range machines[1:] {
597
parent, ok := cache[state.ParentId(machine.Id())]
598
if !ok {
599
panic("We've broken an assumpution.")
602
status := makeMachineStatus(
603
machine,
604
idToIpAddresses[machine.Id()],
605
idToDeviceToSpaces[machine.Id()],
606
idToLinkLayerDevices[machine.Id()],
607
)
615
func makeMachineStatus(
616
machine *state.Machine,
617
ipAddresses []*state.Address,
618
spaces map[string]set.Strings,
619
linkLayerDevices []*state.LinkLayerDevice,
620
) (status params.MachineStatus) {
627
status.Jobs = paramsJobsFromJobs(machine.Jobs())
628
status.WantsVote = machine.WantsVote()
629
status.HasVote = machine.HasVote()
630
sInfo, err := machine.InstanceStatus()
631
populateStatusFromStatusInfoAndErr(&status.InstanceStatus, sInfo, err)
632
instid, err := machine.InstanceId()
633
if err == nil {
634
status.InstanceId = instid
635
addr, err := machine.PublicAddress()
636
if err != nil {
637
// Usually this indicates that no addresses have been set on the
638
// machine yet.
639
addr = network.Address{}
643
mAddrs := machine.Addresses()
644
if len(mAddrs) == 0 {
645
logger.Debugf("no IP addresses fetched for machine %q", instid)
646
// At least give it the newly created DNSName address, if it exists.
647
if addr.Value != "" {
648
mAddrs = append(mAddrs, addr)
649
}
650
}
651
for _, mAddr := range mAddrs {
652
switch mAddr.Scope {
653
case network.ScopeMachineLocal, network.ScopeLinkLocal:
654
continue
655
}
656
status.IPAddresses = append(status.IPAddresses, mAddr.Value)
657
}
658
status.NetworkInterfaces = make(map[string]params.NetworkInterface, len(linkLayerDevices))
659
for _, llDev := range linkLayerDevices {
660
device := llDev.Name()
661
ips := []string{}
662
gw := []string{}
663
ns := []string{}
664
sp := make(set.Strings)
665
for _, ipAddress := range ipAddresses {
666
if ipAddress.DeviceName() != device {
667
continue
668
}
669
ips = append(ips, ipAddress.Value())
670
// We don't expect to find more than one
671
// ipAddress on a device with a list of
672
// nameservers, but append in any case.
673
if len(ipAddress.DNSServers()) > 0 {
674
ns = append(ns, ipAddress.DNSServers()...)
675
}
676
// There should only be one gateway per device
677
// (per machine, in fact, as we don't store
678
// metrics). If we find more than one we should
679
// show them all.
680
if ipAddress.GatewayAddress() != "" {
681
gw = append(gw, ipAddress.GatewayAddress())
683
// There should only be one space per address,
684
// but it's technically possible to have more
685
// than one address on an interface. If we find
686
// that happens, we need to show all spaces, to
687
// be safe.
688
sp = spaces[device]
690
status.NetworkInterfaces[device] = params.NetworkInterface{
691
IPAddresses: ips,
692
MACAddress: llDev.MACAddress(),
693
Gateway: strings.Join(gw, " "),
694
DNSNameservers: ns,
695
Space: strings.Join(sp.Values(), " "),
696
IsUp: llDev.IsUp(),
702
status.InstanceId = "pending"
703
} else {
704
status.InstanceId = "error"
705
}
706
}
707
constraints, err := machine.Constraints()
708
if err != nil {
709
if !errors.IsNotFound(err) {
710
status.Constraints = "error"
711
}
712
} else {
713
status.Constraints = constraints.String()
714
}
718
status.Hardware = "error"
719
}
720
} else {
721
status.Hardware = hc.String()
722
}
727
func (context *statusContext) processRelations() []params.RelationStatus {
728
var out []params.RelationStatus
732
var scope charm.RelationScope
733
var relationInterface string
734
for _, ep := range relation.Endpoints() {
736
ApplicationName: ep.ApplicationName,
737
Name: ep.Name,
738
Role: string(ep.Role),
739
Subordinate: context.isSubordinate(&ep),
740
})
741
// these should match on both sides so use the last
742
relationInterface = ep.Interface
743
scope = ep.Scope
744
}
750
Endpoints: eps,
751
}
752
out = append(out, relStatus)
753
}
754
return out
755
}
756
757
// This method exists only to dedup the loaded relations as they will
758
// appear multiple times in context.relations.
759
func (context *statusContext) getAllRelations() []*state.Relation {
760
var out []*state.Relation
761
seenRelations := make(map[int]bool)
762
for _, relations := range context.relations {
763
for _, relation := range relations {
764
if _, found := seenRelations[relation.Id()]; !found {
765
out = append(out, relation)
766
seenRelations[relation.Id()] = true
767
}
768
}
769
}
770
return out
771
}
772
773
func (context *statusContext) isSubordinate(ep *state.Endpoint) bool {
774
application := context.applications[ep.ApplicationName]
775
if application == nil {
781
func isSubordinate(ep *state.Endpoint, application *state.Application) bool {
782
return ep.Scope == charm.ScopeContainer && !application.IsPrincipal()
786
func paramsJobsFromJobs(jobs []state.MachineJob) []multiwatcher.MachineJob {
787
paramsJobs := make([]multiwatcher.MachineJob, len(jobs))
788
for i, machineJob := range jobs {
789
paramsJobs[i] = machineJob.ToParams()
790
}
791
return paramsJobs
792
}
794
func (context *statusContext) processApplications() map[string]params.ApplicationStatus {
795
applicationsMap := make(map[string]params.ApplicationStatus)
796
for _, s := range context.applications {
797
applicationsMap[s.Name()] = context.processApplication(s)
802
func (context *statusContext) processApplication(application *state.Application) params.ApplicationStatus {
803
applicationCharm, _, err := application.Charm()
804
if err != nil {
805
return params.ApplicationStatus{Err: common.ServerError(err)}
806
}
808
var processedStatus = params.ApplicationStatus{
809
Charm: applicationCharm.URL().String(),
810
Series: application.Series(),
811
Exposed: application.IsExposed(),
812
Life: processLife(application),
814
815
if latestCharm, ok := context.latestCharms[*applicationCharm.URL().WithRevision(-1)]; ok && latestCharm != nil {
816
if latestCharm.Revision() > applicationCharm.URL().Revision {
817
processedStatus.CanUpgradeTo = latestCharm.String()
818
}
819
}
820
821
processedStatus.Relations, processedStatus.SubordinateTo, err = context.processApplicationRelations(application)
826
units := context.units[application.Name()]
827
if application.IsPrincipal() {
828
processedStatus.Units = context.processUnits(units, applicationCharm.URL().String())
829
}
830
applicationStatus, err := application.Status()
832
processedStatus.Err = common.ServerError(err)
833
return processedStatus
834
}
835
processedStatus.Status.Status = applicationStatus.Status.String()
836
processedStatus.Status.Info = applicationStatus.Message
837
processedStatus.Status.Data = applicationStatus.Data
838
processedStatus.Status.Since = applicationStatus.Since
839
840
metrics := applicationCharm.Metrics()
841
planRequired := metrics != nil && metrics.Plan != nil && metrics.Plan.Required
842
if planRequired || len(application.MetricCredentials()) > 0 {
843
processedStatus.MeterStatuses = context.processUnitMeterStatuses(units)
845
846
versions := make([]status.StatusInfo, 0, len(units))
847
for _, unit := range units {
848
statuses, err := unit.WorkloadVersionHistory().StatusHistory(
849
status.StatusHistoryFilter{Size: 1},
850
)
855
// Even though we fully expect there to be historical values there,
856
// even the first should be the empty string, the status history
857
// collection is not added to in a transactional manner, so it may be
858
// not there even though we'd really like it to be. Such is mongo.
859
if len(statuses) > 0 {
860
versions = append(versions, statuses[0])
863
if len(versions) > 0 {
864
sort.Sort(bySinceDescending(versions))
865
processedStatus.WorkloadVersion = versions[0].Message
871
func (context *statusContext) processRemoteApplications() map[string]params.RemoteApplicationStatus {
872
applicationsMap := make(map[string]params.RemoteApplicationStatus)
873
for _, s := range context.remoteApplications {
874
applicationsMap[s.Name()] = context.processRemoteApplication(s)
879
func (context *statusContext) processRemoteApplication(application *state.RemoteApplication) (status params.RemoteApplicationStatus) {
881
status.ApplicationName = application.Name()
882
eps, err := application.Endpoints()
883
if err != nil {
884
status.Err = err
885
return
886
}
887
status.Endpoints = make([]params.RemoteEndpoint, len(eps))
888
for i, ep := range eps {
889
status.Endpoints[i] = params.RemoteEndpoint{
890
Name: ep.Name,
891
Interface: ep.Interface,
892
Role: ep.Role,
893
}
894
}
902
applicationStatus, err := application.Status()
903
populateStatusFromStatusInfoAndErr(&status.Status, applicationStatus, err)
907
func isColorStatus(code state.MeterStatusCode) bool {
908
return code == state.MeterGreen || code == state.MeterAmber || code == state.MeterRed
909
}
910
911
func (context *statusContext) processUnitMeterStatuses(units map[string]*state.Unit) map[string]params.MeterStatus {
912
unitsMap := make(map[string]params.MeterStatus)
918
if isColorStatus(meterStatus.Code) {
919
unitsMap[unit.Name()] = params.MeterStatus{Color: strings.ToLower(meterStatus.Code.String()), Message: meterStatus.Info}
920
}
921
}
922
if len(unitsMap) > 0 {
923
return unitsMap
924
}
925
return nil
926
}
927
928
func (context *statusContext) processUnits(units map[string]*state.Unit, applicationCharm string) map[string]params.UnitStatus {
936
func (context *statusContext) processUnit(unit *state.Unit, applicationCharm string) params.UnitStatus {
938
addr, err := unit.PublicAddress()
939
if err != nil {
940
// Usually this indicates that no addresses have been set on the
941
// machine yet.
942
addr = network.Address{}
957
workloadVersion, err := unit.WorkloadVersion()
958
if err == nil {
959
result.WorkloadVersion = workloadVersion
960
} else {
961
logger.Debugf("error fetching workload version: %v", err)
962
}
963
968
for _, name := range subUnits {
969
subUnit := context.unitByName(name)
970
// subUnit may be nil if subordinate was filtered out.
971
if subUnit != nil {
976
if leader := context.leaders[unit.ApplicationName()]; leader == unit.Name() {
977
result.Leader = true
978
}
983
applicationName := strings.Split(name, "/")[0]
984
return context.units[applicationName][name]
987
func (context *statusContext) processApplicationRelations(application *state.Application) (related map[string][]string, subord []string, err error) {
993
if err != nil {
994
return nil, nil, err
995
}
996
relationName := ep.Relation.Name
1008
for relationName, applicationNames := range related {
1009
sn := set.NewStrings(applicationNames...)
1010
related[relationName] = sn.SortedValues()
1011
}
1012
return related, subordSet.SortedValues(), nil
1013
}
1014
1015
func (context *statusContext) processRemoteApplicationRelations(application *state.RemoteApplication) (related map[string][]string, err error) {
1032
for relationName, applicationNames := range related {
1033
sn := set.NewStrings(applicationNames...)
1034
related[relationName] = sn.SortedValues()
1035
}
1036
return related, nil
1037
}
1038
1043
// processUnitAndAgentStatus retrieves status information for both unit and unitAgents.
1044
func processUnitAndAgentStatus(unit *state.Unit, unitStatus *params.UnitStatus) {
1045
unitStatus.AgentStatus, unitStatus.WorkloadStatus = processUnit(unit)
1048
// populateStatusFromStatusInfoAndErr creates AgentStatus from the typical output
1049
// of a status getter.
1050
func populateStatusFromStatusInfoAndErr(agent *params.DetailedStatus, statusInfo status.StatusInfo, err error) {
1053
agent.Info = statusInfo.Message
1054
agent.Data = filterStatusData(statusInfo.Data)
1055
agent.Since = statusInfo.Since
1058
// processMachine retrieves version and status information for the given machine.
1059
// It also returns deprecated legacy status information.
1060
func processMachine(machine *state.Machine) (out params.DetailedStatus) {
1061
statusInfo, err := common.MachineStatus(machine)
1062
populateStatusFromStatusInfoAndErr(&out, statusInfo, err)
1063
1069
return
1070
}
1071
1072
// processUnit retrieves version and status information for the given unit.
1073
func processUnit(unit *state.Unit) (agentStatus, workloadStatus params.DetailedStatus) {
1074
agent, workload := common.UnitStatus(unit)
1075
populateStatusFromStatusInfoAndErr(&agentStatus, agent.Status, agent.Err)
1076
populateStatusFromStatusInfoAndErr(&workloadStatus, workload.Status, workload.Err)
1077
1080
if t, err := unit.AgentTools(); err == nil {
1081
agentStatus.Version = t.Version.Number.String()
1086
// filterStatusData limits what agent StatusData data is passed over
1087
// the API. This prevents unintended leakage of internal-only data.
1088
func filterStatusData(status map[string]interface{}) map[string]interface{} {
1089
out := make(map[string]interface{})
1090
for name, value := range status {
1091
// use a set here if we end up with a larger whitelist
1092
if name == "relation-id" {
1093
out[name] = value
1094
}
1095
}
1096
return out
1097
}
1098
1099
func processLife(entity lifer) string {
1100
if life := entity.Life(); life != state.Alive {
1101
// alive is the usual state so omit it by default.
1102
return life.String()
1103
}
1104
return ""
1105
}
1106
1107
type bySinceDescending []status.StatusInfo
1108
1109
// Len implements sort.Interface.
1110
func (s bySinceDescending) Len() int { return len(s) }
1111
1112
// Swap implements sort.Interface.
1113
func (s bySinceDescending) Swap(a, b int) { s[a], s[b] = s[b], s[a] }
1114
1115
// Less implements sort.Interface.
1116
func (s bySinceDescending) Less(a, b int) bool { return s[a].Since.After(*s[b].Since) }