Permalink
Newer
Older
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 (
8
"sort"
11
"github.com/juju/errors"
@wallyworld
Oct 31, 2016
12
"github.com/juju/utils/featureflag"
13
"github.com/juju/utils/set"
14
"gopkg.in/juju/charm.v6-unstable"
@wallyworld
Oct 18, 2016
15
"gopkg.in/juju/names.v2"
@wallyworld
Oct 18, 2016
17
"github.com/juju/juju/apiserver/common"
@davecheney
Sep 2, 2014
18
"github.com/juju/juju/apiserver/params"
@wallyworld
Oct 31, 2016
19
"github.com/juju/juju/feature"
20
"github.com/juju/juju/network"
21
"github.com/juju/juju/state"
22
"github.com/juju/juju/state/multiwatcher"
@wallyworld
Oct 18, 2016
23
"github.com/juju/juju/status"
@wallyworld
Oct 18, 2016
26
func agentStatusFromStatusInfo(s []status.StatusInfo, kind status.HistoryKind) []params.DetailedStatus {
27
result := []params.DetailedStatus{}
28
for _, v := range s {
@wallyworld
Oct 18, 2016
29
result = append(result, params.DetailedStatus{
30
Status: string(v.Status),
31
Info: v.Message,
32
Data: v.Data,
33
Since: v.Since,
@wallyworld
Oct 18, 2016
34
Kind: string(kind),
35
})
36
}
37
return result
38
39
}
40
@wallyworld
Oct 18, 2016
41
type byTime []params.DetailedStatus
@wallyworld
Oct 18, 2016
43
func (s byTime) Len() int {
44
return len(s)
45
}
@wallyworld
Oct 18, 2016
46
func (s byTime) Swap(i, j int) {
47
s[i], s[j] = s[j], s[i]
48
}
@wallyworld
Oct 18, 2016
49
func (s byTime) Less(i, j int) bool {
50
return s[i].Since.Before(*s[j].Since)
51
}
52
@wallyworld
Oct 18, 2016
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())
56
if err != nil {
@wallyworld
Oct 18, 2016
57
return nil, errors.Trace(err)
@wallyworld
Oct 18, 2016
59
statuses := []params.DetailedStatus{}
60
if kind == status.KindUnit || kind == status.KindWorkload {
61
unitStatuses, err := unit.StatusHistory(filter)
62
if err != nil {
@wallyworld
Oct 18, 2016
63
return nil, errors.Trace(err)
@wallyworld
Oct 18, 2016
65
statuses = agentStatusFromStatusInfo(unitStatuses, status.KindWorkload)
66
@wallyworld
Oct 18, 2016
68
if kind == status.KindUnit || kind == status.KindUnitAgent {
69
agentStatuses, err := unit.AgentHistory().StatusHistory(filter)
70
if err != nil {
@wallyworld
Oct 18, 2016
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:]
80
}
81
}
82
@wallyworld
Oct 18, 2016
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...),
@wallyworld
Oct 18, 2016
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
123
124
}
125
@wallyworld
Oct 18, 2016
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
})
@wallyworld
Oct 18, 2016
163
return results
164
}
165
166
// FullStatus gives the information needed for juju status over the api
167
func (c *Client) FullStatus(args params.StatusParams) (params.FullStatus, error) {
@wallyworld
Oct 18, 2016
168
if err := c.checkCanRead(); err != nil {
169
return params.FullStatus{}, err
@wallyworld
Oct 18, 2016
171
172
var noStatus params.FullStatus
173
var context statusContext
@wallyworld
Oct 18, 2016
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
}
@wallyworld
Oct 31, 2016
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
}
@wallyworld
Oct 18, 2016
184
}
185
if context.machines, err = fetchMachines(c.api.stateAccessor, nil); err != nil {
186
return noStatus, errors.Annotate(err, "could not fetch machines")
@wallyworld
Oct 18, 2016
187
}
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
}
@wallyworld
Oct 18, 2016
193
if context.relations, err = fetchRelations(c.api.stateAccessor); err != nil {
194
return noStatus, errors.Annotate(err, "could not fetch relations")
@wallyworld
Oct 18, 2016
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
}
@wallyworld
Oct 18, 2016
202
logger.Debugf("Applications: %v", context.applications)
203
logger.Debugf("Remote applications: %v", context.remoteApplications)
205
if len(args.Patterns) > 0 {
206
predicate := BuildPredicateFor(args.Patterns)
@wallyworld
Oct 18, 2016
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
225
// Filter units
@wallyworld
Oct 18, 2016
226
matchedSvcs := make(set.Strings)
227
unitChainPredicate := UnitChainPredicateFn(predicate, context.unitByName)
228
for _, unitMap := range context.units {
229
for name, unit := range unitMap {
@wallyworld
Oct 18, 2016
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
241
// before we discover its parent is a match.
242
if !unit.IsPrincipal() {
243
continue
244
} else if matches, err := unitChainPredicate(unit); err != nil {
245
return noStatus, errors.Annotate(err, "could not filter units")
247
delete(unitMap, name)
@wallyworld
Oct 18, 2016
250
matchedSvcs.Add(unit.ApplicationName())
251
if machineId != "" {
252
matchedMachines.Add(machineId)
254
}
255
}
256
@wallyworld
Oct 18, 2016
257
// Filter applications
258
for svcName, svc := range context.applications {
259
if matchedSvcs.Contains(svcName) {
260
// There are matched units for this application.
261
continue
262
} else if matches, err := predicate(svc); err != nil {
@wallyworld
Oct 18, 2016
263
return noStatus, errors.Annotate(err, "could not filter applications")
264
} else if !matches {
@wallyworld
Oct 18, 2016
265
delete(context.applications, svcName)
266
}
267
}
268
@wallyworld
Oct 18, 2016
269
// TODO(wallyworld) - filter remote applications
271
// Filter machines
272
for status, machineList := range context.machines {
@wallyworld
Oct 18, 2016
273
matched := make([]*state.Machine, 0, len(machineList))
274
for _, m := range machineList {
275
machineContainers, err := m.Containers()
276
if err != nil {
277
return noStatus, err
278
}
279
machineContainersSet := set.NewStrings(machineContainers...)
280
@wallyworld
Oct 18, 2016
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)
@wallyworld
Oct 18, 2016
289
context.machines[status] = matched
291
}
@wallyworld
Oct 18, 2016
293
modelStatus, err := c.modelStatus()
294
if err != nil {
@wallyworld
Oct 18, 2016
295
return noStatus, errors.Annotate(err, "cannot determine model status")
298
Model: modelStatus,
299
Machines: processMachines(
300
context.machines,
301
context.ipAddresses,
302
context.spaces,
303
context.linkLayerDevices,
304
),
@wallyworld
Oct 18, 2016
305
Applications: context.processApplications(),
306
RemoteApplications: context.processRemoteApplications(),
307
Relations: context.processRelations(),
311
// newToolsVersionAvailable will return a string representing a tools
312
// version only if the latest check is newer than current tools.
@wallyworld
Oct 18, 2016
313
func (c *Client) modelStatus() (params.ModelStatusInfo, error) {
314
var info params.ModelStatusInfo
315
316
m, err := c.api.stateAccessor.Model()
317
if err != nil {
@wallyworld
Oct 18, 2016
318
return info, errors.Annotate(err, "cannot get model")
@wallyworld
Oct 18, 2016
320
info.Name = m.Name()
321
info.CloudTag = names.NewCloudTag(m.Cloud()).String()
322
info.CloudRegion = m.CloudRegion()
@wallyworld
Oct 18, 2016
324
cfg, err := m.Config()
325
if err != nil {
@natefinch
Dec 8, 2016
326
return params.ModelStatusInfo{}, errors.Annotate(err, "cannot obtain current model config")
@wallyworld
Oct 18, 2016
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
}
@wallyworld
Oct 18, 2016
337
@natefinch
Dec 8, 2016
338
status, err := m.Status()
@wallyworld
Oct 18, 2016
339
if err != nil {
@natefinch
Dec 8, 2016
340
return params.ModelStatusInfo{}, errors.Annotate(err, "cannot obtain model status info")
@wallyworld
Oct 18, 2016
342
@natefinch
Dec 8, 2016
343
info.ModelStatus = params.DetailedStatus{
344
Status: status.Status.String(),
345
Info: status.Message,
346
Since: status.Since,
347
Data: status.Data,
@natefinch
Dec 8, 2016
350
return info, nil
352
353
type statusContext struct {
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
@wallyworld
Oct 18, 2016
367
// applications: application name -> application
368
applications map[string]*state.Application
@wallyworld
Oct 18, 2016
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.
@wallyworld
Oct 18, 2016
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
410
// fetchNetworkInterfaces returns maps from machine id to ip.addresses, machine
411
// id to a map of interface names from space names, and machine id to
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
@wallyworld
Oct 18, 2016
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,
@wallyworld
Oct 18, 2016
481
) (map[string]*state.Application, map[string]map[string]*state.Unit, map[charm.URL]*state.Charm, error) {
@wallyworld
Oct 18, 2016
483
appMap := make(map[string]*state.Application)
484
unitMap := make(map[string]map[string]*state.Unit)
@wallyworld
Oct 18, 2016
485
latestCharms := make(map[charm.URL]*state.Charm)
486
applications, err := st.AllApplications()
487
if err != nil {
488
return nil, nil, nil, err
489
}
@wallyworld
Oct 18, 2016
490
for _, s := range applications {
491
units, err := s.AllUnits()
492
if err != nil {
493
return nil, nil, nil, err
494
}
@wallyworld
Oct 18, 2016
495
appUnitMap := make(map[string]*state.Unit)
496
for _, u := range units {
@wallyworld
Oct 18, 2016
497
appUnitMap[u.Name()] = u
@wallyworld
Oct 18, 2016
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" {
@wallyworld
Oct 18, 2016
506
latestCharms[*charmURL.WithRevision(-1)] = nil
510
for baseURL := range latestCharms {
511
ch, err := st.LatestPlaceholderCharm(&baseURL)
Apr 17, 2014
512
if errors.IsNotFound(err) {
513
continue
514
}
515
if err != nil {
516
return nil, nil, nil, err
517
}
@wallyworld
Oct 18, 2016
518
latestCharms[baseURL] = ch
@wallyworld
Oct 18, 2016
520
521
return appMap, unitMap, latestCharms, nil
@wallyworld
Oct 18, 2016
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()
528
if err != nil {
529
return nil, err
530
}
531
for _, a := range applications {
532
if _, ok := a.URL(); !ok {
533
continue
534
}
535
appMap[a.Name()] = a
@wallyworld
Oct 18, 2016
537
return appMap, nil
@wallyworld
Oct 18, 2016
540
// fetchRelations returns a map of all relations keyed by application name.
@wallyworld
Oct 18, 2016
542
// This structure is useful for processApplicationRelations() which needs
543
// to have the relations for each application. Reading them once here
544
// avoids the repeated DB hits to retrieve the relations for each
@wallyworld
Oct 18, 2016
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() {
@wallyworld
Oct 18, 2016
554
out[ep.ApplicationName] = append(out[ep.ApplicationName], relation)
555
}
556
}
557
return out, nil
558
}
559
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
585
// Element 0 is assumed to be the top-level machine.
@wallyworld
Oct 18, 2016
586
tlMachine := machines[0]
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
)
608
parent.Containers[machine.Id()] = status
609
cache[machine.Id()] = status
612
return machinesMap
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) {
@wallyworld
Oct 18, 2016
621
var err error
622
status.Id = machine.Id()
@wallyworld
Oct 18, 2016
623
agentStatus := processMachine(machine)
624
status.AgentStatus = agentStatus
626
status.Series = machine.Series()
627
status.Jobs = paramsJobsFromJobs(machine.Jobs())
628
status.WantsVote = machine.WantsVote()
629
status.HasVote = machine.HasVote()
@wallyworld
Oct 18, 2016
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{}
640
logger.Debugf("error fetching public address: %q", err)
641
}
642
status.DNSName = addr.Value
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]
@wallyworld
Oct 18, 2016
689
}
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(),
@wallyworld
Oct 18, 2016
697
}
698
}
699
logger.Debugf("NetworkInterfaces: %+v", status.NetworkInterfaces)
701
if errors.IsNotProvisioned(err) {
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
}
715
hc, err := machine.HardwareCharacteristics()
716
if err != nil {
Apr 17, 2014
717
if !errors.IsNotFound(err) {
718
status.Hardware = "error"
719
}
720
} else {
721
status.Hardware = hc.String()
722
}
723
status.Containers = make(map[string]params.MachineStatus)
724
return
725
}
726
727
func (context *statusContext) processRelations() []params.RelationStatus {
728
var out []params.RelationStatus
729
relations := context.getAllRelations()
730
for _, relation := range relations {
731
var eps []params.EndpointStatus
732
var scope charm.RelationScope
733
var relationInterface string
734
for _, ep := range relation.Endpoints() {
735
eps = append(eps, params.EndpointStatus{
@wallyworld
Oct 18, 2016
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
}
745
relStatus := params.RelationStatus{
746
Id: relation.Id(),
747
Key: relation.String(),
748
Interface: relationInterface,
@wallyworld
Oct 18, 2016
749
Scope: string(scope),
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 {
@wallyworld
Oct 18, 2016
774
application := context.applications[ep.ApplicationName]
775
if application == nil {
776
return false
777
}
@wallyworld
Oct 18, 2016
778
return isSubordinate(ep, application)
@wallyworld
Oct 18, 2016
781
func isSubordinate(ep *state.Endpoint, application *state.Application) bool {
782
return ep.Scope == charm.ScopeContainer && !application.IsPrincipal()
785
// paramsJobsFromJobs converts state jobs to params jobs.
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
}
@wallyworld
Oct 18, 2016
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)
@wallyworld
Oct 18, 2016
799
return applicationsMap
@wallyworld
Oct 18, 2016
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
}
@wallyworld
Oct 18, 2016
808
var processedStatus = params.ApplicationStatus{
809
Charm: applicationCharm.URL().String(),
810
Series: application.Series(),
811
Exposed: application.IsExposed(),
812
Life: processLife(application),
@wallyworld
Oct 18, 2016
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)
822
if err != nil {
@wallyworld
Oct 18, 2016
823
processedStatus.Err = common.ServerError(err)
824
return processedStatus
@wallyworld
Oct 18, 2016
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()
@wallyworld
Oct 18, 2016
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)
@wallyworld
Oct 18, 2016
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
)
@wallyworld
Oct 18, 2016
852
processedStatus.Err = common.ServerError(err)
853
return processedStatus
@wallyworld
Oct 18, 2016
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])
@wallyworld
Oct 18, 2016
863
if len(versions) > 0 {
864
sort.Sort(bySinceDescending(versions))
865
processedStatus.WorkloadVersion = versions[0].Message
@wallyworld
Oct 18, 2016
867
868
return processedStatus
@wallyworld
Oct 18, 2016
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)
@wallyworld
Oct 18, 2016
876
return applicationsMap
@wallyworld
Oct 18, 2016
879
func (context *statusContext) processRemoteApplication(application *state.RemoteApplication) (status params.RemoteApplicationStatus) {
880
status.ApplicationURL, _ = application.URL()
@wallyworld
Oct 18, 2016
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
}
@wallyworld
Oct 18, 2016
895
status.Life = processLife(application)
@wallyworld
Oct 18, 2016
897
status.Relations, err = context.processRemoteApplicationRelations(application)
898
if err != nil {
899
status.Err = err
900
return
901
}
@wallyworld
Oct 18, 2016
902
applicationStatus, err := application.Status()
903
populateStatusFromStatusInfoAndErr(&status.Status, applicationStatus, err)
904
return status
905
}
906
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)
913
for _, unit := range units {
@wallyworld
Oct 18, 2016
914
meterStatus, err := unit.GetMeterStatus()
915
if err != nil {
916
continue
917
}
@wallyworld
Oct 18, 2016
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
@wallyworld
Oct 18, 2016
928
func (context *statusContext) processUnits(units map[string]*state.Unit, applicationCharm string) map[string]params.UnitStatus {
929
unitsMap := make(map[string]params.UnitStatus)
930
for _, unit := range units {
@wallyworld
Oct 18, 2016
931
unitsMap[unit.Name()] = context.processUnit(unit, applicationCharm)
932
}
933
return unitsMap
934
}
935
@wallyworld
Oct 18, 2016
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{}
943
logger.Debugf("error fetching public address: %v", err)
944
}
945
result.PublicAddress = addr.Value
946
unitPorts, _ := unit.OpenedPorts()
947
for _, port := range unitPorts {
948
result.OpenedPorts = append(result.OpenedPorts, port.String())
949
}
950
if unit.IsPrincipal() {
951
result.Machine, _ = unit.AssignedMachineId()
952
}
953
curl, _ := unit.CharmURL()
@wallyworld
Oct 18, 2016
954
if applicationCharm != "" && curl != nil && curl.String() != applicationCharm {
@wallyworld
Oct 18, 2016
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
964
processUnitAndAgentStatus(unit, &result)
966
if subUnits := unit.SubordinateNames(); len(subUnits) > 0 {
967
result.Subordinates = make(map[string]params.UnitStatus)
968
for _, name := range subUnits {
969
subUnit := context.unitByName(name)
970
// subUnit may be nil if subordinate was filtered out.
971
if subUnit != nil {
@wallyworld
Oct 18, 2016
972
result.Subordinates[name] = context.processUnit(subUnit, applicationCharm)
@wallyworld
Oct 18, 2016
976
if leader := context.leaders[unit.ApplicationName()]; leader == unit.Name() {
977
result.Leader = true
978
}
980
}
981
982
func (context *statusContext) unitByName(name string) *state.Unit {
@wallyworld
Oct 18, 2016
983
applicationName := strings.Split(name, "/")[0]
984
return context.units[applicationName][name]
@wallyworld
Oct 18, 2016
987
func (context *statusContext) processApplicationRelations(application *state.Application) (related map[string][]string, subord []string, err error) {
@davecheney
Nov 13, 2014
988
subordSet := make(set.Strings)
989
related = make(map[string][]string)
@wallyworld
Oct 18, 2016
990
relations := context.relations[application.Name()]
991
for _, relation := range relations {
@wallyworld
Oct 18, 2016
992
ep, err := relation.Endpoint(application.Name())
993
if err != nil {
994
return nil, nil, err
995
}
996
relationName := ep.Relation.Name
@wallyworld
Oct 18, 2016
997
eps, err := relation.RelatedEndpoints(application.Name())
998
if err != nil {
999
return nil, nil, err
1000
}
1001
for _, ep := range eps {
@wallyworld
Oct 18, 2016
1002
if isSubordinate(&ep, application) {
1003
subordSet.Add(ep.ApplicationName)
@wallyworld
Oct 18, 2016
1005
related[relationName] = append(related[relationName], ep.ApplicationName)
@wallyworld
Oct 18, 2016
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
@wallyworld
Oct 18, 2016
1015
func (context *statusContext) processRemoteApplicationRelations(application *state.RemoteApplication) (related map[string][]string, err error) {
1016
related = make(map[string][]string)
@wallyworld
Oct 18, 2016
1017
relations := context.relations[application.Name()]
1018
for _, relation := range relations {
@wallyworld
Oct 18, 2016
1019
ep, err := relation.Endpoint(application.Name())
1020
if err != nil {
1021
return nil, err
1022
}
1023
relationName := ep.Relation.Name
@wallyworld
Oct 18, 2016
1024
eps, err := relation.RelatedEndpoints(application.Name())
1025
if err != nil {
1026
return nil, err
1027
}
1028
for _, ep := range eps {
@wallyworld
Oct 18, 2016
1029
related[relationName] = append(related[relationName], ep.ApplicationName)
1030
}
1031
}
@wallyworld
Oct 18, 2016
1032
for relationName, applicationNames := range related {
1033
sn := set.NewStrings(applicationNames...)
1034
related[relationName] = sn.SortedValues()
1035
}
1036
return related, nil
1037
}
1038
1039
type lifer interface {
1040
Life() state.Life
1041
}
1042
1043
// processUnitAndAgentStatus retrieves status information for both unit and unitAgents.
@wallyworld
Oct 18, 2016
1044
func processUnitAndAgentStatus(unit *state.Unit, unitStatus *params.UnitStatus) {
1045
unitStatus.AgentStatus, unitStatus.WorkloadStatus = processUnit(unit)
1046
}
1047
@wallyworld
Oct 18, 2016
1048
// populateStatusFromStatusInfoAndErr creates AgentStatus from the typical output
1049
// of a status getter.
1050
func populateStatusFromStatusInfoAndErr(agent *params.DetailedStatus, statusInfo status.StatusInfo, err error) {
@wallyworld
Oct 18, 2016
1052
agent.Status = statusInfo.Status.String()
1053
agent.Info = statusInfo.Message
1054
agent.Data = filterStatusData(statusInfo.Data)
1055
agent.Since = statusInfo.Since
1056
}
1057
1058
// processMachine retrieves version and status information for the given machine.
1059
// It also returns deprecated legacy status information.
@wallyworld
Oct 18, 2016
1060
func processMachine(machine *state.Machine) (out params.DetailedStatus) {
1061
statusInfo, err := common.MachineStatus(machine)
1062
populateStatusFromStatusInfoAndErr(&out, statusInfo, err)
1063
1064
out.Life = processLife(machine)
1066
if t, err := machine.AgentTools(); err == nil {
1067
out.Version = t.Version.Number.String()
1069
return
1070
}
1071
1072
// processUnit retrieves version and status information for the given unit.
@wallyworld
Oct 18, 2016
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
1078
agentStatus.Life = processLife(unit)
@wallyworld
Oct 18, 2016
1079
1080
if t, err := unit.AgentTools(); err == nil {
1081
agentStatus.Version = t.Version.Number.String()
1082
}
1083
return
1084
}
1085
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
}
@wallyworld
Oct 18, 2016
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) }