~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/state/machine.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2012, 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package state
 
5
 
 
6
import (
 
7
        "fmt"
 
8
        "strings"
 
9
        "time"
 
10
 
 
11
        "github.com/juju/errors"
 
12
        jujutxn "github.com/juju/txn"
 
13
        "github.com/juju/utils"
 
14
        "github.com/juju/utils/set"
 
15
        "github.com/juju/version"
 
16
        "gopkg.in/juju/names.v2"
 
17
        "gopkg.in/mgo.v2"
 
18
        "gopkg.in/mgo.v2/bson"
 
19
        "gopkg.in/mgo.v2/txn"
 
20
 
 
21
        "github.com/juju/juju/constraints"
 
22
        "github.com/juju/juju/core/actions"
 
23
        "github.com/juju/juju/instance"
 
24
        "github.com/juju/juju/mongo"
 
25
        "github.com/juju/juju/network"
 
26
        "github.com/juju/juju/state/multiwatcher"
 
27
        "github.com/juju/juju/state/presence"
 
28
        "github.com/juju/juju/status"
 
29
        "github.com/juju/juju/tools"
 
30
)
 
31
 
 
32
// Machine represents the state of a machine.
 
33
type Machine struct {
 
34
        st  *State
 
35
        doc machineDoc
 
36
}
 
37
 
 
38
// MachineJob values define responsibilities that machines may be
 
39
// expected to fulfil.
 
40
type MachineJob int
 
41
 
 
42
const (
 
43
        _ MachineJob = iota
 
44
        JobHostUnits
 
45
        JobManageModel
 
46
)
 
47
 
 
48
var (
 
49
        jobNames = map[MachineJob]multiwatcher.MachineJob{
 
50
                JobHostUnits:   multiwatcher.JobHostUnits,
 
51
                JobManageModel: multiwatcher.JobManageModel,
 
52
        }
 
53
        jobMigrationValue = map[MachineJob]string{
 
54
                JobHostUnits:   "host-units",
 
55
                JobManageModel: "api-server",
 
56
        }
 
57
)
 
58
 
 
59
// AllJobs returns all supported machine jobs.
 
60
func AllJobs() []MachineJob {
 
61
        return []MachineJob{
 
62
                JobHostUnits,
 
63
                JobManageModel,
 
64
        }
 
65
}
 
66
 
 
67
// ToParams returns the job as multiwatcher.MachineJob.
 
68
func (job MachineJob) ToParams() multiwatcher.MachineJob {
 
69
        if jujuJob, ok := jobNames[job]; ok {
 
70
                return jujuJob
 
71
        }
 
72
        return multiwatcher.MachineJob(fmt.Sprintf("<unknown job %d>", int(job)))
 
73
}
 
74
 
 
75
// params.JobsFromJobs converts state jobs to juju jobs.
 
76
func paramsJobsFromJobs(jobs []MachineJob) []multiwatcher.MachineJob {
 
77
        jujuJobs := make([]multiwatcher.MachineJob, len(jobs))
 
78
        for i, machineJob := range jobs {
 
79
                jujuJobs[i] = machineJob.ToParams()
 
80
        }
 
81
        return jujuJobs
 
82
}
 
83
 
 
84
// MigrationValue converts the state job into a useful human readable
 
85
// string for model migration.
 
86
func (job MachineJob) MigrationValue() string {
 
87
        if value, ok := jobMigrationValue[job]; ok {
 
88
                return value
 
89
        }
 
90
        return "unknown"
 
91
}
 
92
 
 
93
func (job MachineJob) String() string {
 
94
        return string(job.ToParams())
 
95
}
 
96
 
 
97
// manualMachinePrefix signals as prefix of Nonce that a machine is
 
98
// manually provisioned.
 
99
const manualMachinePrefix = "manual:"
 
100
 
 
101
// machineDoc represents the internal state of a machine in MongoDB.
 
102
// Note the correspondence with MachineInfo in apiserver/juju.
 
103
type machineDoc struct {
 
104
        DocID         string `bson:"_id"`
 
105
        Id            string `bson:"machineid"`
 
106
        ModelUUID     string `bson:"model-uuid"`
 
107
        Nonce         string
 
108
        Series        string
 
109
        ContainerType string
 
110
        Principals    []string
 
111
        Life          Life
 
112
        Tools         *tools.Tools `bson:",omitempty"`
 
113
        Jobs          []MachineJob
 
114
        NoVote        bool
 
115
        HasVote       bool
 
116
        PasswordHash  string
 
117
        Clean         bool
 
118
 
 
119
        // TODO(axw) 2015-06-22 #1467379
 
120
        // We need an upgrade step to populate "volumes" and "filesystems"
 
121
        // for entities created in 1.24.
 
122
        //
 
123
        // Volumes contains the names of volumes attached to the machine.
 
124
        Volumes []string `bson:"volumes,omitempty"`
 
125
        // Filesystems contains the names of filesystems attached to the machine.
 
126
        Filesystems []string `bson:"filesystems,omitempty"`
 
127
 
 
128
        // We store 2 different sets of addresses for the machine, obtained
 
129
        // from different sources.
 
130
        // Addresses is the set of addresses obtained by asking the provider.
 
131
        Addresses []address
 
132
 
 
133
        // MachineAddresses is the set of addresses obtained from the machine itself.
 
134
        MachineAddresses []address
 
135
 
 
136
        // PreferredPublicAddress is the preferred address to be used for
 
137
        // the machine when a public address is requested.
 
138
        PreferredPublicAddress address `bson:",omitempty"`
 
139
 
 
140
        // PreferredPrivateAddress is the preferred address to be used for
 
141
        // the machine when a private address is requested.
 
142
        PreferredPrivateAddress address `bson:",omitempty"`
 
143
 
 
144
        // The SupportedContainers attributes are used to advertise what containers this
 
145
        // machine is capable of hosting.
 
146
        SupportedContainersKnown bool
 
147
        SupportedContainers      []instance.ContainerType `bson:",omitempty"`
 
148
        // Placement is the placement directive that should be used when provisioning
 
149
        // an instance for the machine.
 
150
        Placement string `bson:",omitempty"`
 
151
 
 
152
        // StopMongoUntilVersion holds the version that must be checked to
 
153
        // know if mongo must be stopped.
 
154
        StopMongoUntilVersion string `bson:",omitempty"`
 
155
}
 
156
 
 
157
func newMachine(st *State, doc *machineDoc) *Machine {
 
158
        machine := &Machine{
 
159
                st:  st,
 
160
                doc: *doc,
 
161
        }
 
162
        return machine
 
163
}
 
164
 
 
165
func wantsVote(jobs []MachineJob, noVote bool) bool {
 
166
        return hasJob(jobs, JobManageModel) && !noVote
 
167
}
 
168
 
 
169
// Id returns the machine id.
 
170
func (m *Machine) Id() string {
 
171
        return m.doc.Id
 
172
}
 
173
 
 
174
// Principals returns the principals for the machine.
 
175
func (m *Machine) Principals() []string {
 
176
        return m.doc.Principals
 
177
}
 
178
 
 
179
// Series returns the operating system series running on the machine.
 
180
func (m *Machine) Series() string {
 
181
        return m.doc.Series
 
182
}
 
183
 
 
184
// ContainerType returns the type of container hosting this machine.
 
185
func (m *Machine) ContainerType() instance.ContainerType {
 
186
        return instance.ContainerType(m.doc.ContainerType)
 
187
}
 
188
 
 
189
// machineGlobalKey returns the global database key for the identified machine.
 
190
func machineGlobalKey(id string) string {
 
191
        return "m#" + id
 
192
}
 
193
 
 
194
// machineGlobalInstanceKey returns the global database key for the identified machine's instance.
 
195
func machineGlobalInstanceKey(id string) string {
 
196
        return machineGlobalKey(id) + "#instance"
 
197
}
 
198
 
 
199
// globalInstanceKey returns the global database key for the machinei's instance.
 
200
func (m *Machine) globalInstanceKey() string {
 
201
        return machineGlobalInstanceKey(m.doc.Id)
 
202
}
 
203
 
 
204
// globalKey returns the global database key for the machine.
 
205
func (m *Machine) globalKey() string {
 
206
        return machineGlobalKey(m.doc.Id)
 
207
}
 
208
 
 
209
// instanceData holds attributes relevant to a provisioned machine.
 
210
type instanceData struct {
 
211
        DocID      string      `bson:"_id"`
 
212
        MachineId  string      `bson:"machineid"`
 
213
        InstanceId instance.Id `bson:"instanceid"`
 
214
        ModelUUID  string      `bson:"model-uuid"`
 
215
        Status     string      `bson:"status,omitempty"`
 
216
        Arch       *string     `bson:"arch,omitempty"`
 
217
        Mem        *uint64     `bson:"mem,omitempty"`
 
218
        RootDisk   *uint64     `bson:"rootdisk,omitempty"`
 
219
        CpuCores   *uint64     `bson:"cpucores,omitempty"`
 
220
        CpuPower   *uint64     `bson:"cpupower,omitempty"`
 
221
        Tags       *[]string   `bson:"tags,omitempty"`
 
222
        AvailZone  *string     `bson:"availzone,omitempty"`
 
223
}
 
224
 
 
225
func hardwareCharacteristics(instData instanceData) *instance.HardwareCharacteristics {
 
226
        return &instance.HardwareCharacteristics{
 
227
                Arch:             instData.Arch,
 
228
                Mem:              instData.Mem,
 
229
                RootDisk:         instData.RootDisk,
 
230
                CpuCores:         instData.CpuCores,
 
231
                CpuPower:         instData.CpuPower,
 
232
                Tags:             instData.Tags,
 
233
                AvailabilityZone: instData.AvailZone,
 
234
        }
 
235
}
 
236
 
 
237
// TODO(wallyworld): move this method to a service.
 
238
func (m *Machine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) {
 
239
        instData, err := getInstanceData(m.st, m.Id())
 
240
        if err != nil {
 
241
                return nil, err
 
242
        }
 
243
        return hardwareCharacteristics(instData), nil
 
244
}
 
245
 
 
246
func getInstanceData(st *State, id string) (instanceData, error) {
 
247
        instanceDataCollection, closer := st.getCollection(instanceDataC)
 
248
        defer closer()
 
249
 
 
250
        var instData instanceData
 
251
        err := instanceDataCollection.FindId(id).One(&instData)
 
252
        if err == mgo.ErrNotFound {
 
253
                return instanceData{}, errors.NotFoundf("instance data for machine %v", id)
 
254
        }
 
255
        if err != nil {
 
256
                return instanceData{}, fmt.Errorf("cannot get instance data for machine %v: %v", id, err)
 
257
        }
 
258
        return instData, nil
 
259
}
 
260
 
 
261
// Tag returns a tag identifying the machine. The String method provides a
 
262
// string representation that is safe to use as a file name. The returned name
 
263
// will be different from other Tag values returned by any other entities
 
264
// from the same state.
 
265
func (m *Machine) Tag() names.Tag {
 
266
        return m.MachineTag()
 
267
}
 
268
 
 
269
// MachineTag returns the more specific MachineTag type as opposed
 
270
// to the more generic Tag type.
 
271
func (m *Machine) MachineTag() names.MachineTag {
 
272
        return names.NewMachineTag(m.Id())
 
273
}
 
274
 
 
275
// Life returns whether the machine is Alive, Dying or Dead.
 
276
func (m *Machine) Life() Life {
 
277
        return m.doc.Life
 
278
}
 
279
 
 
280
// Jobs returns the responsibilities that must be fulfilled by m's agent.
 
281
func (m *Machine) Jobs() []MachineJob {
 
282
        return m.doc.Jobs
 
283
}
 
284
 
 
285
// WantsVote reports whether the machine is a controller
 
286
// that wants to take part in peer voting.
 
287
func (m *Machine) WantsVote() bool {
 
288
        return wantsVote(m.doc.Jobs, m.doc.NoVote)
 
289
}
 
290
 
 
291
// HasVote reports whether that machine is currently a voting
 
292
// member of the replica set.
 
293
func (m *Machine) HasVote() bool {
 
294
        return m.doc.HasVote
 
295
}
 
296
 
 
297
// SetHasVote sets whether the machine is currently a voting
 
298
// member of the replica set. It should only be called
 
299
// from the worker that maintains the replica set.
 
300
func (m *Machine) SetHasVote(hasVote bool) error {
 
301
        ops := []txn.Op{{
 
302
                C:      machinesC,
 
303
                Id:     m.doc.DocID,
 
304
                Assert: notDeadDoc,
 
305
                Update: bson.D{{"$set", bson.D{{"hasvote", hasVote}}}},
 
306
        }}
 
307
        if err := m.st.runTransaction(ops); err != nil {
 
308
                return fmt.Errorf("cannot set HasVote of machine %v: %v", m, onAbort(err, ErrDead))
 
309
        }
 
310
        m.doc.HasVote = hasVote
 
311
        return nil
 
312
}
 
313
 
 
314
// SetStopMongoUntilVersion sets a version that is to be checked against
 
315
// the agent config before deciding if mongo must be started on a
 
316
// state server.
 
317
func (m *Machine) SetStopMongoUntilVersion(v mongo.Version) error {
 
318
        ops := []txn.Op{{
 
319
                C:      machinesC,
 
320
                Id:     m.doc.DocID,
 
321
                Update: bson.D{{"$set", bson.D{{"stopmongountilversion", v.String()}}}},
 
322
        }}
 
323
        if err := m.st.runTransaction(ops); err != nil {
 
324
                return fmt.Errorf("cannot set StopMongoUntilVersion %v: %v", m, onAbort(err, ErrDead))
 
325
        }
 
326
        m.doc.StopMongoUntilVersion = v.String()
 
327
        return nil
 
328
}
 
329
 
 
330
// StopMongoUntilVersion returns the current minimum version that
 
331
// is required for this machine to have mongo running.
 
332
func (m *Machine) StopMongoUntilVersion() (mongo.Version, error) {
 
333
        return mongo.NewVersion(m.doc.StopMongoUntilVersion)
 
334
}
 
335
 
 
336
// IsManager returns true if the machine has JobManageModel.
 
337
func (m *Machine) IsManager() bool {
 
338
        return hasJob(m.doc.Jobs, JobManageModel)
 
339
}
 
340
 
 
341
// IsManual returns true if the machine was manually provisioned.
 
342
func (m *Machine) IsManual() (bool, error) {
 
343
        // Apart from the bootstrap machine, manually provisioned
 
344
        // machines have a nonce prefixed with "manual:". This is
 
345
        // unique to manual provisioning.
 
346
        if strings.HasPrefix(m.doc.Nonce, manualMachinePrefix) {
 
347
                return true, nil
 
348
        }
 
349
        // The bootstrap machine uses BootstrapNonce, so in that
 
350
        // case we need to check if its provider type is "manual".
 
351
        // We also check for "null", which is an alias for manual.
 
352
        if m.doc.Id == "0" {
 
353
                cfg, err := m.st.ModelConfig()
 
354
                if err != nil {
 
355
                        return false, err
 
356
                }
 
357
                t := cfg.Type()
 
358
                return t == "null" || t == "manual", nil
 
359
        }
 
360
        return false, nil
 
361
}
 
362
 
 
363
// AgentTools returns the tools that the agent is currently running.
 
364
// It returns an error that satisfies errors.IsNotFound if the tools
 
365
// have not yet been set.
 
366
func (m *Machine) AgentTools() (*tools.Tools, error) {
 
367
        if m.doc.Tools == nil {
 
368
                return nil, errors.NotFoundf("agent tools for machine %v", m)
 
369
        }
 
370
        tools := *m.doc.Tools
 
371
        return &tools, nil
 
372
}
 
373
 
 
374
// checkVersionValidity checks whether the given version is suitable
 
375
// for passing to SetAgentVersion.
 
376
func checkVersionValidity(v version.Binary) error {
 
377
        if v.Series == "" || v.Arch == "" {
 
378
                return fmt.Errorf("empty series or arch")
 
379
        }
 
380
        return nil
 
381
}
 
382
 
 
383
// SetAgentVersion sets the version of juju that the agent is
 
384
// currently running.
 
385
func (m *Machine) SetAgentVersion(v version.Binary) (err error) {
 
386
        defer errors.DeferredAnnotatef(&err, "cannot set agent version for machine %v", m)
 
387
        if err = checkVersionValidity(v); err != nil {
 
388
                return err
 
389
        }
 
390
        tools := &tools.Tools{Version: v}
 
391
        ops := []txn.Op{{
 
392
                C:      machinesC,
 
393
                Id:     m.doc.DocID,
 
394
                Assert: notDeadDoc,
 
395
                Update: bson.D{{"$set", bson.D{{"tools", tools}}}},
 
396
        }}
 
397
        // A "raw" transaction is needed here because this function gets
 
398
        // called before database migraions have run so we don't
 
399
        // necessarily want the env UUID added to the id.
 
400
        if err := m.st.runRawTransaction(ops); err != nil {
 
401
                return onAbort(err, ErrDead)
 
402
        }
 
403
        m.doc.Tools = tools
 
404
        return nil
 
405
}
 
406
 
 
407
// SetMongoPassword sets the password the agent responsible for the machine
 
408
// should use to communicate with the controllers.  Previous passwords
 
409
// are invalidated.
 
410
func (m *Machine) SetMongoPassword(password string) error {
 
411
        if !m.IsManager() {
 
412
                return errors.NotSupportedf("setting mongo password for non-controller machine %v", m)
 
413
        }
 
414
        return mongo.SetAdminMongoPassword(m.st.session, m.Tag().String(), password)
 
415
}
 
416
 
 
417
// SetPassword sets the password for the machine's agent.
 
418
func (m *Machine) SetPassword(password string) error {
 
419
        if len(password) < utils.MinAgentPasswordLength {
 
420
                return fmt.Errorf("password is only %d bytes long, and is not a valid Agent password", len(password))
 
421
        }
 
422
        return m.setPasswordHash(utils.AgentPasswordHash(password))
 
423
}
 
424
 
 
425
// setPasswordHash sets the underlying password hash in the database directly
 
426
// to the value supplied. This is split out from SetPassword to allow direct
 
427
// manipulation in tests (to check for backwards compatibility).
 
428
func (m *Machine) setPasswordHash(passwordHash string) error {
 
429
        ops := []txn.Op{{
 
430
                C:      machinesC,
 
431
                Id:     m.doc.DocID,
 
432
                Assert: notDeadDoc,
 
433
                Update: bson.D{{"$set", bson.D{{"passwordhash", passwordHash}}}},
 
434
        }}
 
435
        // A "raw" transaction is used here because this code has to work
 
436
        // before the machine env UUID DB migration has run. In this case
 
437
        // we don't want the automatic env UUID prefixing to the doc _id
 
438
        // to occur.
 
439
        if err := m.st.runRawTransaction(ops); err != nil {
 
440
                return fmt.Errorf("cannot set password of machine %v: %v", m, onAbort(err, ErrDead))
 
441
        }
 
442
        m.doc.PasswordHash = passwordHash
 
443
        return nil
 
444
}
 
445
 
 
446
// Return the underlying PasswordHash stored in the database. Used by the test
 
447
// suite to check that the PasswordHash gets properly updated to new values
 
448
// when compatibility mode is detected.
 
449
func (m *Machine) getPasswordHash() string {
 
450
        return m.doc.PasswordHash
 
451
}
 
452
 
 
453
// PasswordValid returns whether the given password is valid
 
454
// for the given machine.
 
455
func (m *Machine) PasswordValid(password string) bool {
 
456
        agentHash := utils.AgentPasswordHash(password)
 
457
        return agentHash == m.doc.PasswordHash
 
458
}
 
459
 
 
460
// Destroy sets the machine lifecycle to Dying if it is Alive. It does
 
461
// nothing otherwise. Destroy will fail if the machine has principal
 
462
// units assigned, or if the machine has JobManageModel.
 
463
// If the machine has assigned units, Destroy will return
 
464
// a HasAssignedUnitsError.
 
465
func (m *Machine) Destroy() error {
 
466
        return m.advanceLifecycle(Dying)
 
467
}
 
468
 
 
469
// ForceDestroy queues the machine for complete removal, including the
 
470
// destruction of all units and containers on the machine.
 
471
func (m *Machine) ForceDestroy() error {
 
472
        ops, err := m.forceDestroyOps()
 
473
        if err != nil {
 
474
                return errors.Trace(err)
 
475
        }
 
476
        if err := m.st.runTransaction(ops); err != txn.ErrAborted {
 
477
                return errors.Trace(err)
 
478
        }
 
479
        return nil
 
480
}
 
481
 
 
482
var managerMachineError = errors.New("machine is required by the model")
 
483
 
 
484
func (m *Machine) forceDestroyOps() ([]txn.Op, error) {
 
485
        if m.IsManager() {
 
486
                return nil, errors.Trace(managerMachineError)
 
487
        }
 
488
 
 
489
        return []txn.Op{{
 
490
                C:      machinesC,
 
491
                Id:     m.doc.DocID,
 
492
                Assert: bson.D{{"jobs", bson.D{{"$nin", []MachineJob{JobManageModel}}}}},
 
493
        }, m.st.newCleanupOp(cleanupForceDestroyedMachine, m.doc.Id)}, nil
 
494
}
 
495
 
 
496
// EnsureDead sets the machine lifecycle to Dead if it is Alive or Dying.
 
497
// It does nothing otherwise. EnsureDead will fail if the machine has
 
498
// principal units assigned, or if the machine has JobManageModel.
 
499
// If the machine has assigned units, EnsureDead will return
 
500
// a HasAssignedUnitsError.
 
501
func (m *Machine) EnsureDead() error {
 
502
        return m.advanceLifecycle(Dead)
 
503
}
 
504
 
 
505
type HasAssignedUnitsError struct {
 
506
        MachineId string
 
507
        UnitNames []string
 
508
}
 
509
 
 
510
func (e *HasAssignedUnitsError) Error() string {
 
511
        return fmt.Sprintf("machine %s has unit %q assigned", e.MachineId, e.UnitNames[0])
 
512
}
 
513
 
 
514
func IsHasAssignedUnitsError(err error) bool {
 
515
        _, ok := err.(*HasAssignedUnitsError)
 
516
        return ok
 
517
}
 
518
 
 
519
// Containers returns the container ids belonging to a parent machine.
 
520
// TODO(wallyworld): move this method to a service
 
521
func (m *Machine) Containers() ([]string, error) {
 
522
        containerRefs, closer := m.st.getCollection(containerRefsC)
 
523
        defer closer()
 
524
 
 
525
        var mc machineContainers
 
526
        err := containerRefs.FindId(m.doc.DocID).One(&mc)
 
527
        if err == nil {
 
528
                return mc.Children, nil
 
529
        }
 
530
        if err == mgo.ErrNotFound {
 
531
                return nil, errors.NotFoundf("container info for machine %v", m.Id())
 
532
        }
 
533
        return nil, err
 
534
}
 
535
 
 
536
// ParentId returns the Id of the host machine if this machine is a container.
 
537
func (m *Machine) ParentId() (string, bool) {
 
538
        parentId := ParentId(m.Id())
 
539
        return parentId, parentId != ""
 
540
}
 
541
 
 
542
// IsContainer returns true if the machine is a container.
 
543
func (m *Machine) IsContainer() bool {
 
544
        _, isContainer := m.ParentId()
 
545
        return isContainer
 
546
}
 
547
 
 
548
type HasContainersError struct {
 
549
        MachineId    string
 
550
        ContainerIds []string
 
551
}
 
552
 
 
553
func (e *HasContainersError) Error() string {
 
554
        return fmt.Sprintf("machine %s is hosting containers %q", e.MachineId, strings.Join(e.ContainerIds, ","))
 
555
}
 
556
 
 
557
// IsHasContainersError reports whether or not the error is a
 
558
// HasContainersError, indicating that an attempt to destroy
 
559
// a machine failed due to it having containers.
 
560
func IsHasContainersError(err error) bool {
 
561
        _, ok := errors.Cause(err).(*HasContainersError)
 
562
        return ok
 
563
}
 
564
 
 
565
// HasAttachmentsError is the error returned by EnsureDead if the machine
 
566
// has attachments to resources that must be cleaned up first.
 
567
type HasAttachmentsError struct {
 
568
        MachineId   string
 
569
        Attachments []names.Tag
 
570
}
 
571
 
 
572
func (e *HasAttachmentsError) Error() string {
 
573
        return fmt.Sprintf(
 
574
                "machine %s has attachments %s",
 
575
                e.MachineId, e.Attachments,
 
576
        )
 
577
}
 
578
 
 
579
// IsHasAttachmentsError reports whether or not the error is a
 
580
// HasAttachmentsError, indicating that an attempt to destroy
 
581
// a machine failed due to it having storage attachments.
 
582
func IsHasAttachmentsError(err error) bool {
 
583
        _, ok := errors.Cause(err).(*HasAttachmentsError)
 
584
        return ok
 
585
}
 
586
 
 
587
// advanceLifecycle ensures that the machine's lifecycle is no earlier
 
588
// than the supplied value. If the machine already has that lifecycle
 
589
// value, or a later one, no changes will be made to remote state. If
 
590
// the machine has any responsibilities that preclude a valid change in
 
591
// lifecycle, it will return an error.
 
592
func (original *Machine) advanceLifecycle(life Life) (err error) {
 
593
        containers, err := original.Containers()
 
594
        if err != nil {
 
595
                return err
 
596
        }
 
597
        if len(containers) > 0 {
 
598
                return &HasContainersError{
 
599
                        MachineId:    original.doc.Id,
 
600
                        ContainerIds: containers,
 
601
                }
 
602
        }
 
603
        m := original
 
604
        defer func() {
 
605
                if err == nil {
 
606
                        // The machine's lifecycle is known to have advanced; it may be
 
607
                        // known to have already advanced further than requested, in
 
608
                        // which case we set the latest known valid value.
 
609
                        if m == nil {
 
610
                                life = Dead
 
611
                        } else if m.doc.Life > life {
 
612
                                life = m.doc.Life
 
613
                        }
 
614
                        original.doc.Life = life
 
615
                }
 
616
        }()
 
617
        // op and
 
618
        op := txn.Op{
 
619
                C:      machinesC,
 
620
                Id:     m.doc.DocID,
 
621
                Update: bson.D{{"$set", bson.D{{"life", life}}}},
 
622
        }
 
623
        // noUnits asserts that the machine has no principal units.
 
624
        noUnits := bson.DocElem{
 
625
                "$or", []bson.D{
 
626
                        {{"principals", bson.D{{"$size", 0}}}},
 
627
                        {{"principals", bson.D{{"$exists", false}}}},
 
628
                },
 
629
        }
 
630
        cleanupOp := m.st.newCleanupOp(cleanupDyingMachine, m.doc.Id)
 
631
        // multiple attempts: one with original data, one with refreshed data, and a final
 
632
        // one intended to determine the cause of failure of the preceding attempt.
 
633
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
634
                advanceAsserts := bson.D{
 
635
                        {"jobs", bson.D{{"$nin", []MachineJob{JobManageModel}}}},
 
636
                        {"hasvote", bson.D{{"$ne", true}}},
 
637
                }
 
638
                // Grab a fresh copy of the machine data.
 
639
                // We don't write to original, because the expectation is that state-
 
640
                // changing methods only set the requested change on the receiver; a case
 
641
                // could perhaps be made that this is not a helpful convention in the
 
642
                // context of the new state API, but we maintain consistency in the
 
643
                // face of uncertainty.
 
644
                if m, err = m.st.Machine(m.doc.Id); errors.IsNotFound(err) {
 
645
                        return nil, jujutxn.ErrNoOperations
 
646
                } else if err != nil {
 
647
                        return nil, err
 
648
                }
 
649
                // Check that the life change is sane, and collect the assertions
 
650
                // necessary to determine that it remains so.
 
651
                switch life {
 
652
                case Dying:
 
653
                        if m.doc.Life != Alive {
 
654
                                return nil, jujutxn.ErrNoOperations
 
655
                        }
 
656
                        advanceAsserts = append(advanceAsserts, isAliveDoc...)
 
657
                case Dead:
 
658
                        if m.doc.Life == Dead {
 
659
                                return nil, jujutxn.ErrNoOperations
 
660
                        }
 
661
                        advanceAsserts = append(advanceAsserts, notDeadDoc...)
 
662
                default:
 
663
                        panic(fmt.Errorf("cannot advance lifecycle to %v", life))
 
664
                }
 
665
                // Check that the machine does not have any responsibilities that
 
666
                // prevent a lifecycle change.
 
667
                if hasJob(m.doc.Jobs, JobManageModel) {
 
668
                        // (NOTE: When we enable multiple JobManageModel machines,
 
669
                        // this restriction will be lifted, but we will assert that the
 
670
                        // machine is not voting)
 
671
                        return nil, fmt.Errorf("machine %s is required by the model", m.doc.Id)
 
672
                }
 
673
                if m.doc.HasVote {
 
674
                        return nil, fmt.Errorf("machine %s is a voting replica set member", m.doc.Id)
 
675
                }
 
676
                // If there are no alive units left on the machine, or all the services are dying,
 
677
                // then the machine may be soon destroyed by a cleanup worker.
 
678
                // In that case, we don't want to return any error about not being able to
 
679
                // destroy a machine with units as it will be a lie.
 
680
                if life == Dying {
 
681
                        canDie := true
 
682
                        var principalUnitnames []string
 
683
                        for _, principalUnit := range m.doc.Principals {
 
684
                                principalUnitnames = append(principalUnitnames, principalUnit)
 
685
                                u, err := m.st.Unit(principalUnit)
 
686
                                if err != nil {
 
687
                                        return nil, errors.Annotatef(err, "reading machine %s principal unit %v", m, m.doc.Principals[0])
 
688
                                }
 
689
                                svc, err := u.Application()
 
690
                                if err != nil {
 
691
                                        return nil, errors.Annotatef(err, "reading machine %s principal unit service %v", m, u.doc.Application)
 
692
                                }
 
693
                                if u.Life() == Alive && svc.Life() == Alive {
 
694
                                        canDie = false
 
695
                                        break
 
696
                                }
 
697
                        }
 
698
                        if canDie {
 
699
                                containers, err := m.Containers()
 
700
                                if err != nil {
 
701
                                        return nil, errors.Annotatef(err, "reading machine %s containers", m)
 
702
                                }
 
703
                                canDie = len(containers) == 0
 
704
                        }
 
705
                        if canDie {
 
706
                                checkUnits := bson.DocElem{
 
707
                                        "$or", []bson.D{
 
708
                                                {{"principals", principalUnitnames}},
 
709
                                                {{"principals", bson.D{{"$size", 0}}}},
 
710
                                                {{"principals", bson.D{{"$exists", false}}}},
 
711
                                        },
 
712
                                }
 
713
                                op.Assert = append(advanceAsserts, checkUnits)
 
714
                                containerCheck := txn.Op{
 
715
                                        C:  containerRefsC,
 
716
                                        Id: m.doc.DocID,
 
717
                                        Assert: bson.D{{"$or", []bson.D{
 
718
                                                {{"children", bson.D{{"$size", 0}}}},
 
719
                                                {{"children", bson.D{{"$exists", false}}}},
 
720
                                        }}},
 
721
                                }
 
722
                                return []txn.Op{op, containerCheck, cleanupOp}, nil
 
723
                        }
 
724
                }
 
725
 
 
726
                if len(m.doc.Principals) > 0 {
 
727
                        return nil, &HasAssignedUnitsError{
 
728
                                MachineId: m.doc.Id,
 
729
                                UnitNames: m.doc.Principals,
 
730
                        }
 
731
                }
 
732
                advanceAsserts = append(advanceAsserts, noUnits)
 
733
 
 
734
                if life == Dead {
 
735
                        // A machine may not become Dead until it has no more
 
736
                        // attachments to inherently machine-bound storage.
 
737
                        storageAsserts, err := m.assertNoPersistentStorage()
 
738
                        if err != nil {
 
739
                                return nil, errors.Trace(err)
 
740
                        }
 
741
                        advanceAsserts = append(advanceAsserts, storageAsserts...)
 
742
                }
 
743
 
 
744
                // Add the additional asserts needed for this transaction.
 
745
                op.Assert = advanceAsserts
 
746
                return []txn.Op{op, cleanupOp}, nil
 
747
        }
 
748
        if err = m.st.run(buildTxn); err == jujutxn.ErrExcessiveContention {
 
749
                err = errors.Annotatef(err, "machine %s cannot advance lifecycle", m)
 
750
        }
 
751
        return err
 
752
}
 
753
 
 
754
// assertNoPersistentStorage ensures that there are no persistent volumes or
 
755
// filesystems attached to the machine, and returns any mgo/txn assertions
 
756
// required to ensure that remains true.
 
757
func (m *Machine) assertNoPersistentStorage() (bson.D, error) {
 
758
        attachments := make(set.Tags)
 
759
        for _, v := range m.doc.Volumes {
 
760
                tag := names.NewVolumeTag(v)
 
761
                machineBound, err := isVolumeInherentlyMachineBound(m.st, tag)
 
762
                if err != nil {
 
763
                        return nil, errors.Trace(err)
 
764
                }
 
765
                if !machineBound {
 
766
                        attachments.Add(tag)
 
767
                }
 
768
        }
 
769
        for _, f := range m.doc.Filesystems {
 
770
                tag := names.NewFilesystemTag(f)
 
771
                machineBound, err := isFilesystemInherentlyMachineBound(m.st, tag)
 
772
                if err != nil {
 
773
                        return nil, errors.Trace(err)
 
774
                }
 
775
                if !machineBound {
 
776
                        attachments.Add(tag)
 
777
                }
 
778
        }
 
779
        if len(attachments) > 0 {
 
780
                return nil, &HasAttachmentsError{
 
781
                        MachineId:   m.doc.Id,
 
782
                        Attachments: attachments.SortedValues(),
 
783
                }
 
784
        }
 
785
        if m.doc.Life == Dying {
 
786
                return nil, nil
 
787
        }
 
788
        // A Dying machine cannot have attachments added to it,
 
789
        // but if we're advancing from Alive to Dead then we
 
790
        // must ensure no concurrent attachments are made.
 
791
        noNewVolumes := bson.DocElem{
 
792
                "volumes", bson.D{{
 
793
                        "$not", bson.D{{
 
794
                                "$elemMatch", bson.D{{
 
795
                                        "$nin", m.doc.Volumes,
 
796
                                }},
 
797
                        }},
 
798
                }},
 
799
                // There are no volumes that are not in
 
800
                // the set of volumes we previously knew
 
801
                // about => the current set of volumes
 
802
                // is a subset of the previously known set.
 
803
        }
 
804
        noNewFilesystems := bson.DocElem{
 
805
                "filesystems", bson.D{{
 
806
                        "$not", bson.D{{
 
807
                                "$elemMatch", bson.D{{
 
808
                                        "$nin", m.doc.Filesystems,
 
809
                                }},
 
810
                        }},
 
811
                }},
 
812
        }
 
813
        return bson.D{noNewVolumes, noNewFilesystems}, nil
 
814
}
 
815
 
 
816
func (m *Machine) removePortsOps() ([]txn.Op, error) {
 
817
        if m.doc.Life != Dead {
 
818
                return nil, errors.Errorf("machine is not dead")
 
819
        }
 
820
        ports, err := m.AllPorts()
 
821
        if err != nil {
 
822
                return nil, err
 
823
        }
 
824
        var ops []txn.Op
 
825
        for _, p := range ports {
 
826
                ops = append(ops, p.removeOps()...)
 
827
        }
 
828
        return ops, nil
 
829
}
 
830
 
 
831
func (m *Machine) removeOps() ([]txn.Op, error) {
 
832
        if m.doc.Life != Dead {
 
833
                return nil, fmt.Errorf("machine is not dead")
 
834
        }
 
835
        ops := []txn.Op{
 
836
                {
 
837
                        C:      machinesC,
 
838
                        Id:     m.doc.DocID,
 
839
                        Assert: txn.DocExists,
 
840
                        Remove: true,
 
841
                },
 
842
                {
 
843
                        C:      machinesC,
 
844
                        Id:     m.doc.DocID,
 
845
                        Assert: isDeadDoc,
 
846
                },
 
847
                removeStatusOp(m.st, m.globalKey()),
 
848
                removeStatusOp(m.st, m.globalInstanceKey()),
 
849
                removeConstraintsOp(m.st, m.globalKey()),
 
850
                annotationRemoveOp(m.st, m.globalKey()),
 
851
                removeRebootDocOp(m.st, m.globalKey()),
 
852
                removeMachineBlockDevicesOp(m.Id()),
 
853
                removeModelMachineRefOp(m.st, m.Id()),
 
854
                removeSSHHostKeyOp(m.st, m.globalKey()),
 
855
        }
 
856
        linkLayerDevicesOps, err := m.removeAllLinkLayerDevicesOps()
 
857
        if err != nil {
 
858
                return nil, errors.Trace(err)
 
859
        }
 
860
        devicesAddressesOps, err := m.removeAllAddressesOps()
 
861
        if err != nil {
 
862
                return nil, errors.Trace(err)
 
863
        }
 
864
        portsOps, err := m.removePortsOps()
 
865
        if err != nil {
 
866
                return nil, errors.Trace(err)
 
867
        }
 
868
        filesystemOps, err := m.st.removeMachineFilesystemsOps(m.MachineTag())
 
869
        if err != nil {
 
870
                return nil, errors.Trace(err)
 
871
        }
 
872
        volumeOps, err := m.st.removeMachineVolumesOps(m.MachineTag())
 
873
        if err != nil {
 
874
                return nil, errors.Trace(err)
 
875
        }
 
876
        ops = append(ops, linkLayerDevicesOps...)
 
877
        ops = append(ops, devicesAddressesOps...)
 
878
        ops = append(ops, portsOps...)
 
879
        ops = append(ops, removeContainerRefOps(m.st, m.Id())...)
 
880
        ops = append(ops, filesystemOps...)
 
881
        ops = append(ops, volumeOps...)
 
882
        return ops, nil
 
883
}
 
884
 
 
885
// Remove removes the machine from state. It will fail if the machine
 
886
// is not Dead.
 
887
func (m *Machine) Remove() (err error) {
 
888
        defer errors.DeferredAnnotatef(&err, "cannot remove machine %s", m.doc.Id)
 
889
        logger.Tracef("removing machine %q", m.Id())
 
890
        // Local variable so we can re-get the machine without disrupting
 
891
        // the caller.
 
892
        machine := m
 
893
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
894
                if attempt != 0 {
 
895
                        machine, err = machine.st.Machine(machine.Id())
 
896
                        if errors.IsNotFound(err) {
 
897
                                // The machine's gone away, that's fine.
 
898
                                return nil, jujutxn.ErrNoOperations
 
899
                        }
 
900
                        if err != nil {
 
901
                                return nil, errors.Trace(err)
 
902
                        }
 
903
                }
 
904
                ops, err := machine.removeOps()
 
905
                if err != nil {
 
906
                        return nil, errors.Trace(err)
 
907
                }
 
908
                return ops, nil
 
909
        }
 
910
        return m.st.run(buildTxn)
 
911
}
 
912
 
 
913
// Refresh refreshes the contents of the machine from the underlying
 
914
// state. It returns an error that satisfies errors.IsNotFound if the
 
915
// machine has been removed.
 
916
func (m *Machine) Refresh() error {
 
917
        mdoc, err := m.st.getMachineDoc(m.Id())
 
918
        if err != nil {
 
919
                if errors.IsNotFound(err) {
 
920
                        return err
 
921
                }
 
922
                return errors.Annotatef(err, "cannot refresh machine %v", m)
 
923
        }
 
924
        m.doc = *mdoc
 
925
        return nil
 
926
}
 
927
 
 
928
// AgentPresence returns whether the respective remote agent is alive.
 
929
func (m *Machine) AgentPresence() (bool, error) {
 
930
        pwatcher := m.st.workers.PresenceWatcher()
 
931
        return pwatcher.Alive(m.globalKey())
 
932
}
 
933
 
 
934
// WaitAgentPresence blocks until the respective agent is alive.
 
935
func (m *Machine) WaitAgentPresence(timeout time.Duration) (err error) {
 
936
        defer errors.DeferredAnnotatef(&err, "waiting for agent of machine %v", m)
 
937
        ch := make(chan presence.Change)
 
938
        pwatcher := m.st.workers.PresenceWatcher()
 
939
        pwatcher.Watch(m.globalKey(), ch)
 
940
        defer pwatcher.Unwatch(m.globalKey(), ch)
 
941
        for i := 0; i < 2; i++ {
 
942
                select {
 
943
                case change := <-ch:
 
944
                        if change.Alive {
 
945
                                return nil
 
946
                        }
 
947
                case <-time.After(timeout):
 
948
                        // TODO(fwereade): 2016-03-17 lp:1558657
 
949
                        return fmt.Errorf("still not alive after timeout")
 
950
                case <-pwatcher.Dead():
 
951
                        return pwatcher.Err()
 
952
                }
 
953
        }
 
954
        panic(fmt.Sprintf("presence reported dead status twice in a row for machine %v", m))
 
955
}
 
956
 
 
957
// SetAgentPresence signals that the agent for machine m is alive.
 
958
// It returns the started pinger.
 
959
func (m *Machine) SetAgentPresence() (*presence.Pinger, error) {
 
960
        presenceCollection := m.st.getPresenceCollection()
 
961
        p := presence.NewPinger(presenceCollection, m.st.modelTag, m.globalKey())
 
962
        err := p.Start()
 
963
        if err != nil {
 
964
                return nil, err
 
965
        }
 
966
        // We preform a manual sync here so that the
 
967
        // presence pinger has the most up-to-date information when it
 
968
        // starts. This ensures that commands run immediately after bootstrap
 
969
        // like status or enable-ha will have an accurate values
 
970
        // for agent-state.
 
971
        //
 
972
        // TODO: Does not work for multiple controllers. Trigger a sync across all controllers.
 
973
        if m.IsManager() {
 
974
                m.st.workers.PresenceWatcher().Sync()
 
975
        }
 
976
        return p, nil
 
977
}
 
978
 
 
979
// InstanceId returns the provider specific instance id for this
 
980
// machine, or a NotProvisionedError, if not set.
 
981
func (m *Machine) InstanceId() (instance.Id, error) {
 
982
        instData, err := getInstanceData(m.st, m.Id())
 
983
        if errors.IsNotFound(err) {
 
984
                err = errors.NotProvisionedf("machine %v", m.Id())
 
985
        }
 
986
        if err != nil {
 
987
                return "", err
 
988
        }
 
989
        return instData.InstanceId, err
 
990
}
 
991
 
 
992
// InstanceStatus returns the provider specific instance status for this machine,
 
993
// or a NotProvisionedError if instance is not yet provisioned.
 
994
func (m *Machine) InstanceStatus() (status.StatusInfo, error) {
 
995
        machineStatus, err := getStatus(m.st, m.globalInstanceKey(), "instance")
 
996
        if err != nil {
 
997
                logger.Warningf("error when retrieving instance status for machine: %s, %v", m.Id(), err)
 
998
                return status.StatusInfo{}, err
 
999
        }
 
1000
        return machineStatus, nil
 
1001
}
 
1002
 
 
1003
// SetInstanceStatus sets the provider specific instance status for a machine.
 
1004
func (m *Machine) SetInstanceStatus(sInfo status.StatusInfo) (err error) {
 
1005
        return setStatus(m.st, setStatusParams{
 
1006
                badge:     "instance",
 
1007
                globalKey: m.globalInstanceKey(),
 
1008
                status:    sInfo.Status,
 
1009
                message:   sInfo.Message,
 
1010
                rawData:   sInfo.Data,
 
1011
                updated:   sInfo.Since,
 
1012
        })
 
1013
 
 
1014
}
 
1015
 
 
1016
// InstanceStatusHistory returns a slice of at most filter.Size StatusInfo items
 
1017
// or items as old as filter.Date or items newer than now - filter.Delta time
 
1018
// representing past statuses for this machine instance.
 
1019
// Instance represents the provider underlying [v]hardware or container where
 
1020
// this juju machine is deployed.
 
1021
func (m *Machine) InstanceStatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) {
 
1022
        args := &statusHistoryArgs{
 
1023
                st:        m.st,
 
1024
                globalKey: m.globalInstanceKey(),
 
1025
                filter:    filter,
 
1026
        }
 
1027
        return statusHistory(args)
 
1028
}
 
1029
 
 
1030
// AvailabilityZone returns the provier-specific instance availability
 
1031
// zone in which the machine was provisioned.
 
1032
func (m *Machine) AvailabilityZone() (string, error) {
 
1033
        instData, err := getInstanceData(m.st, m.Id())
 
1034
        if errors.IsNotFound(err) {
 
1035
                return "", errors.Trace(errors.NotProvisionedf("machine %v", m.Id()))
 
1036
        }
 
1037
        if err != nil {
 
1038
                return "", errors.Trace(err)
 
1039
        }
 
1040
        var zone string
 
1041
        if instData.AvailZone != nil {
 
1042
                zone = *instData.AvailZone
 
1043
        }
 
1044
        return zone, nil
 
1045
}
 
1046
 
 
1047
// Units returns all the units that have been assigned to the machine.
 
1048
func (m *Machine) Units() (units []*Unit, err error) {
 
1049
        defer errors.DeferredAnnotatef(&err, "cannot get units assigned to machine %v", m)
 
1050
        unitsCollection, closer := m.st.getCollection(unitsC)
 
1051
        defer closer()
 
1052
 
 
1053
        pudocs := []unitDoc{}
 
1054
        err = unitsCollection.Find(bson.D{{"machineid", m.doc.Id}}).All(&pudocs)
 
1055
        if err != nil {
 
1056
                return nil, err
 
1057
        }
 
1058
        for _, pudoc := range pudocs {
 
1059
                units = append(units, newUnit(m.st, &pudoc))
 
1060
                docs := []unitDoc{}
 
1061
                err = unitsCollection.Find(bson.D{{"principal", pudoc.Name}}).All(&docs)
 
1062
                if err != nil {
 
1063
                        return nil, err
 
1064
                }
 
1065
                for _, doc := range docs {
 
1066
                        units = append(units, newUnit(m.st, &doc))
 
1067
                }
 
1068
        }
 
1069
        return units, nil
 
1070
}
 
1071
 
 
1072
// SetProvisioned sets the provider specific machine id, nonce and also metadata for
 
1073
// this machine. Once set, the instance id cannot be changed.
 
1074
//
 
1075
// When provisioning an instance, a nonce should be created and passed
 
1076
// when starting it, before adding the machine to the state. This means
 
1077
// that if the provisioner crashes (or its connection to the state is
 
1078
// lost) after starting the instance, we can be sure that only a single
 
1079
// instance will be able to act for that machine.
 
1080
func (m *Machine) SetProvisioned(id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics) (err error) {
 
1081
        defer errors.DeferredAnnotatef(&err, "cannot set instance data for machine %q", m)
 
1082
 
 
1083
        if id == "" || nonce == "" {
 
1084
                return fmt.Errorf("instance id and nonce cannot be empty")
 
1085
        }
 
1086
 
 
1087
        if characteristics == nil {
 
1088
                characteristics = &instance.HardwareCharacteristics{}
 
1089
        }
 
1090
        instData := &instanceData{
 
1091
                DocID:      m.doc.DocID,
 
1092
                MachineId:  m.doc.Id,
 
1093
                InstanceId: id,
 
1094
                ModelUUID:  m.doc.ModelUUID,
 
1095
                Arch:       characteristics.Arch,
 
1096
                Mem:        characteristics.Mem,
 
1097
                RootDisk:   characteristics.RootDisk,
 
1098
                CpuCores:   characteristics.CpuCores,
 
1099
                CpuPower:   characteristics.CpuPower,
 
1100
                Tags:       characteristics.Tags,
 
1101
                AvailZone:  characteristics.AvailabilityZone,
 
1102
        }
 
1103
 
 
1104
        ops := []txn.Op{
 
1105
                {
 
1106
                        C:      machinesC,
 
1107
                        Id:     m.doc.DocID,
 
1108
                        Assert: append(isAliveDoc, bson.DocElem{"nonce", ""}),
 
1109
                        Update: bson.D{{"$set", bson.D{{"nonce", nonce}}}},
 
1110
                }, {
 
1111
                        C:      instanceDataC,
 
1112
                        Id:     m.doc.DocID,
 
1113
                        Assert: txn.DocMissing,
 
1114
                        Insert: instData,
 
1115
                },
 
1116
        }
 
1117
 
 
1118
        if err = m.st.runTransaction(ops); err == nil {
 
1119
                m.doc.Nonce = nonce
 
1120
                return nil
 
1121
        } else if err != txn.ErrAborted {
 
1122
                return err
 
1123
        } else if alive, err := isAlive(m.st, machinesC, m.doc.DocID); err != nil {
 
1124
                return err
 
1125
        } else if !alive {
 
1126
                return errNotAlive
 
1127
        }
 
1128
        return fmt.Errorf("already set")
 
1129
}
 
1130
 
 
1131
// SetInstanceInfo is used to provision a machine and in one steps set it's
 
1132
// instance id, nonce, hardware characteristics, add link-layer devices and set
 
1133
// their addresses as needed.
 
1134
func (m *Machine) SetInstanceInfo(
 
1135
        id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics,
 
1136
        devicesArgs []LinkLayerDeviceArgs, devicesAddrs []LinkLayerDeviceAddress,
 
1137
        volumes map[names.VolumeTag]VolumeInfo,
 
1138
        volumeAttachments map[names.VolumeTag]VolumeAttachmentInfo,
 
1139
) error {
 
1140
 
 
1141
        if err := m.SetParentLinkLayerDevicesBeforeTheirChildren(devicesArgs); err != nil {
 
1142
                return errors.Trace(err)
 
1143
        }
 
1144
        if err := m.SetDevicesAddressesIdempotently(devicesAddrs); err != nil {
 
1145
                return errors.Trace(err)
 
1146
        }
 
1147
        if err := setProvisionedVolumeInfo(m.st, volumes); err != nil {
 
1148
                return errors.Trace(err)
 
1149
        }
 
1150
        if err := setMachineVolumeAttachmentInfo(m.st, m.Id(), volumeAttachments); err != nil {
 
1151
                return errors.Trace(err)
 
1152
        }
 
1153
        return m.SetProvisioned(id, nonce, characteristics)
 
1154
}
 
1155
 
 
1156
// Addresses returns any hostnames and ips associated with a machine,
 
1157
// determined both by the machine itself, and by asking the provider.
 
1158
//
 
1159
// The addresses returned by the provider shadow any of the addresses
 
1160
// that the machine reported with the same address value.
 
1161
// Provider-reported addresses always come before machine-reported
 
1162
// addresses. Duplicates are removed.
 
1163
func (m *Machine) Addresses() (addresses []network.Address) {
 
1164
        return network.MergedAddresses(networkAddresses(m.doc.MachineAddresses), networkAddresses(m.doc.Addresses))
 
1165
}
 
1166
 
 
1167
func containsAddress(addresses []address, address address) bool {
 
1168
        for _, addr := range addresses {
 
1169
                if addr.Value == address.Value {
 
1170
                        return true
 
1171
                }
 
1172
        }
 
1173
        return false
 
1174
}
 
1175
 
 
1176
// PublicAddress returns a public address for the machine. If no address is
 
1177
// available it returns an error that satisfies network.IsNoAddressError().
 
1178
func (m *Machine) PublicAddress() (network.Address, error) {
 
1179
        publicAddress := m.doc.PreferredPublicAddress.networkAddress()
 
1180
        var err error
 
1181
        if publicAddress.Value == "" {
 
1182
                err = network.NoAddressError("public")
 
1183
        }
 
1184
        return publicAddress, err
 
1185
}
 
1186
 
 
1187
// maybeGetNewAddress determines if the current address is the most appropriate
 
1188
// match, and if not it selects the best from the slice of all available
 
1189
// addresses. It returns the new address and a bool indicating if a different
 
1190
// one was picked.
 
1191
func maybeGetNewAddress(addr address, providerAddresses, machineAddresses []address, getAddr func([]address) network.Address, checkScope func(address) bool) (address, bool) {
 
1192
        // For picking the best address, try provider addresses first.
 
1193
        var newAddr address
 
1194
        netAddr := getAddr(providerAddresses)
 
1195
        if netAddr.Value == "" {
 
1196
                netAddr = getAddr(machineAddresses)
 
1197
                newAddr = fromNetworkAddress(netAddr, OriginMachine)
 
1198
        } else {
 
1199
                newAddr = fromNetworkAddress(netAddr, OriginProvider)
 
1200
        }
 
1201
        // The order of these checks is important. If the stored address is
 
1202
        // empty we *always* want to check for a new address so we do that
 
1203
        // first. If the stored address is unavilable we also *must* check for
 
1204
        // a new address so we do that next. If the original is a machine
 
1205
        // address and a provider address is available we want to switch to
 
1206
        // that. Finally we check to see if a better match on scope from the
 
1207
        // same origin is available.
 
1208
        if addr.Value == "" {
 
1209
                return newAddr, newAddr.Value != ""
 
1210
        }
 
1211
        if !containsAddress(providerAddresses, addr) && !containsAddress(machineAddresses, addr) {
 
1212
                return newAddr, true
 
1213
        }
 
1214
        if Origin(addr.Origin) != OriginProvider && Origin(newAddr.Origin) == OriginProvider {
 
1215
                return newAddr, true
 
1216
        }
 
1217
        if !checkScope(addr) {
 
1218
                // If addr.Origin is machine and newAddr.Origin is provider we will
 
1219
                // have already caught that, and for the inverse we don't want to
 
1220
                // replace the address.
 
1221
                if addr.Origin == newAddr.Origin {
 
1222
                        return newAddr, checkScope(newAddr)
 
1223
                }
 
1224
        }
 
1225
        return addr, false
 
1226
}
 
1227
 
 
1228
// PrivateAddress returns a private address for the machine. If no address is
 
1229
// available it returns an error that satisfies network.IsNoAddressError().
 
1230
func (m *Machine) PrivateAddress() (network.Address, error) {
 
1231
        privateAddress := m.doc.PreferredPrivateAddress.networkAddress()
 
1232
        var err error
 
1233
        if privateAddress.Value == "" {
 
1234
                err = network.NoAddressError("private")
 
1235
        }
 
1236
        return privateAddress, err
 
1237
}
 
1238
 
 
1239
func (m *Machine) setPreferredAddressOps(addr address, isPublic bool) []txn.Op {
 
1240
        fieldName := "preferredprivateaddress"
 
1241
        current := m.doc.PreferredPrivateAddress
 
1242
        if isPublic {
 
1243
                fieldName = "preferredpublicaddress"
 
1244
                current = m.doc.PreferredPublicAddress
 
1245
        }
 
1246
        // Assert that the field is either missing (never been set) or is
 
1247
        // unchanged from its previous value.
 
1248
 
 
1249
        // Since using a struct in the assert also asserts ordering, and we know that mgo
 
1250
        // can change the ordering, we assert on the dotted values, effectively checking each
 
1251
        // of the attributes of the address.
 
1252
        currentD := []bson.D{
 
1253
                {{fieldName + ".value", current.Value}},
 
1254
                {{fieldName + ".addresstype", current.AddressType}},
 
1255
        }
 
1256
        // Since scope, origin, and space have omitempty, we don't add them if they are empty.
 
1257
        if current.Scope != "" {
 
1258
                currentD = append(currentD, bson.D{{fieldName + ".networkscope", current.Scope}})
 
1259
        }
 
1260
        if current.Origin != "" {
 
1261
                currentD = append(currentD, bson.D{{fieldName + ".origin", current.Origin}})
 
1262
        }
 
1263
        if current.SpaceName != "" {
 
1264
                currentD = append(currentD, bson.D{{fieldName + ".spacename", current.SpaceName}})
 
1265
        }
 
1266
 
 
1267
        assert := bson.D{{"$or", []bson.D{
 
1268
                {{"$and", currentD}},
 
1269
                {{fieldName, nil}}}}}
 
1270
 
 
1271
        ops := []txn.Op{{
 
1272
                C:      machinesC,
 
1273
                Id:     m.doc.DocID,
 
1274
                Update: bson.D{{"$set", bson.D{{fieldName, addr}}}},
 
1275
                Assert: assert,
 
1276
        }}
 
1277
        return ops
 
1278
}
 
1279
 
 
1280
func (m *Machine) setPublicAddressOps(providerAddresses []address, machineAddresses []address) ([]txn.Op, address, bool) {
 
1281
        publicAddress := m.doc.PreferredPublicAddress
 
1282
        // Always prefer an exact match if available.
 
1283
        checkScope := func(addr address) bool {
 
1284
                return network.ExactScopeMatch(addr.networkAddress(), network.ScopePublic)
 
1285
        }
 
1286
        // Without an exact match, prefer a fallback match.
 
1287
        getAddr := func(addresses []address) network.Address {
 
1288
                addr, _ := network.SelectPublicAddress(networkAddresses(addresses))
 
1289
                return addr
 
1290
        }
 
1291
 
 
1292
        newAddr, changed := maybeGetNewAddress(publicAddress, providerAddresses, machineAddresses, getAddr, checkScope)
 
1293
        if !changed {
 
1294
                // No change, so no ops.
 
1295
                return []txn.Op{}, publicAddress, false
 
1296
        }
 
1297
 
 
1298
        ops := m.setPreferredAddressOps(newAddr, true)
 
1299
        return ops, newAddr, true
 
1300
}
 
1301
 
 
1302
func (m *Machine) setPrivateAddressOps(providerAddresses []address, machineAddresses []address) ([]txn.Op, address, bool) {
 
1303
        privateAddress := m.doc.PreferredPrivateAddress
 
1304
        // Always prefer an exact match if available.
 
1305
        checkScope := func(addr address) bool {
 
1306
                return network.ExactScopeMatch(addr.networkAddress(), network.ScopeMachineLocal, network.ScopeCloudLocal)
 
1307
        }
 
1308
        // Without an exact match, prefer a fallback match.
 
1309
        getAddr := func(addresses []address) network.Address {
 
1310
                addr, _ := network.SelectInternalAddress(networkAddresses(addresses), false)
 
1311
                return addr
 
1312
        }
 
1313
 
 
1314
        newAddr, changed := maybeGetNewAddress(privateAddress, providerAddresses, machineAddresses, getAddr, checkScope)
 
1315
        if !changed {
 
1316
                // No change, so no ops.
 
1317
                return []txn.Op{}, privateAddress, false
 
1318
        }
 
1319
        ops := m.setPreferredAddressOps(newAddr, false)
 
1320
        return ops, newAddr, true
 
1321
}
 
1322
 
 
1323
// SetProviderAddresses records any addresses related to the machine, sourced
 
1324
// by asking the provider.
 
1325
func (m *Machine) SetProviderAddresses(addresses ...network.Address) (err error) {
 
1326
        mdoc, err := m.st.getMachineDoc(m.Id())
 
1327
        if err != nil {
 
1328
                return errors.Annotatef(err, "cannot refresh provider addresses for machine %s", m)
 
1329
        }
 
1330
        if err = m.setAddresses(addresses, &mdoc.Addresses, "addresses"); err != nil {
 
1331
                return fmt.Errorf("cannot set addresses of machine %v: %v", m, err)
 
1332
        }
 
1333
        m.doc.Addresses = mdoc.Addresses
 
1334
        return nil
 
1335
}
 
1336
 
 
1337
// ProviderAddresses returns any hostnames and ips associated with a machine,
 
1338
// as determined by asking the provider.
 
1339
func (m *Machine) ProviderAddresses() (addresses []network.Address) {
 
1340
        for _, address := range m.doc.Addresses {
 
1341
                addresses = append(addresses, address.networkAddress())
 
1342
        }
 
1343
        return
 
1344
}
 
1345
 
 
1346
// MachineAddresses returns any hostnames and ips associated with a machine,
 
1347
// determined by asking the machine itself.
 
1348
func (m *Machine) MachineAddresses() (addresses []network.Address) {
 
1349
        for _, address := range m.doc.MachineAddresses {
 
1350
                addresses = append(addresses, address.networkAddress())
 
1351
        }
 
1352
        return
 
1353
}
 
1354
 
 
1355
// SetMachineAddresses records any addresses related to the machine, sourced
 
1356
// by asking the machine.
 
1357
func (m *Machine) SetMachineAddresses(addresses ...network.Address) (err error) {
 
1358
        mdoc, err := m.st.getMachineDoc(m.Id())
 
1359
        if err != nil {
 
1360
                return errors.Annotatef(err, "cannot refresh machine addresses for machine %s", m)
 
1361
        }
 
1362
        if err = m.setAddresses(addresses, &mdoc.MachineAddresses, "machineaddresses"); err != nil {
 
1363
                return fmt.Errorf("cannot set machine addresses of machine %v: %v", m, err)
 
1364
        }
 
1365
        m.doc.MachineAddresses = mdoc.MachineAddresses
 
1366
        return nil
 
1367
}
 
1368
 
 
1369
// setAddresses updates the machine's addresses (either Addresses or
 
1370
// MachineAddresses, depending on the field argument). Changes are
 
1371
// only predicated on the machine not being Dead; concurrent address
 
1372
// changes are ignored.
 
1373
func (m *Machine) setAddresses(addresses []network.Address, field *[]address, fieldName string) error {
 
1374
        addressesToSet := make([]network.Address, len(addresses))
 
1375
        copy(addressesToSet, addresses)
 
1376
 
 
1377
        // Update addresses now.
 
1378
        network.SortAddresses(addressesToSet)
 
1379
        origin := OriginProvider
 
1380
        if fieldName == "machineaddresses" {
 
1381
                origin = OriginMachine
 
1382
        }
 
1383
        stateAddresses := fromNetworkAddresses(addressesToSet, origin)
 
1384
 
 
1385
        var (
 
1386
                newPrivate, newPublic         address
 
1387
                changedPrivate, changedPublic bool
 
1388
                err                           error
 
1389
        )
 
1390
        machine := m
 
1391
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
1392
                if attempt != 0 {
 
1393
                        if machine, err = machine.st.Machine(machine.doc.Id); err != nil {
 
1394
                                return nil, err
 
1395
                        }
 
1396
                }
 
1397
                if machine.doc.Life == Dead {
 
1398
                        return nil, errNotAlive
 
1399
                }
 
1400
                ops := []txn.Op{{
 
1401
                        C:      machinesC,
 
1402
                        Id:     machine.doc.DocID,
 
1403
                        Assert: notDeadDoc,
 
1404
                        Update: bson.D{{"$set", bson.D{{fieldName, stateAddresses}}}},
 
1405
                }}
 
1406
 
 
1407
                var providerAddresses []address
 
1408
                var machineAddresses []address
 
1409
                if fieldName == "machineaddresses" {
 
1410
                        providerAddresses = machine.doc.Addresses
 
1411
                        machineAddresses = stateAddresses
 
1412
                } else {
 
1413
                        machineAddresses = machine.doc.MachineAddresses
 
1414
                        providerAddresses = stateAddresses
 
1415
                }
 
1416
 
 
1417
                var setPrivateAddressOps, setPublicAddressOps []txn.Op
 
1418
                setPrivateAddressOps, newPrivate, changedPrivate = machine.setPrivateAddressOps(providerAddresses, machineAddresses)
 
1419
                setPublicAddressOps, newPublic, changedPublic = machine.setPublicAddressOps(providerAddresses, machineAddresses)
 
1420
                ops = append(ops, setPrivateAddressOps...)
 
1421
                ops = append(ops, setPublicAddressOps...)
 
1422
                return ops, nil
 
1423
        }
 
1424
        err = m.st.run(buildTxn)
 
1425
        if err == txn.ErrAborted {
 
1426
                return ErrDead
 
1427
        } else if err != nil {
 
1428
                return errors.Trace(err)
 
1429
        }
 
1430
 
 
1431
        *field = stateAddresses
 
1432
        if changedPrivate {
 
1433
                m.doc.PreferredPrivateAddress = newPrivate
 
1434
        }
 
1435
        if changedPublic {
 
1436
                m.doc.PreferredPublicAddress = newPublic
 
1437
        }
 
1438
        return nil
 
1439
}
 
1440
 
 
1441
// CheckProvisioned returns true if the machine was provisioned with the given nonce.
 
1442
func (m *Machine) CheckProvisioned(nonce string) bool {
 
1443
        return nonce == m.doc.Nonce && nonce != ""
 
1444
}
 
1445
 
 
1446
// String returns a unique description of this machine.
 
1447
func (m *Machine) String() string {
 
1448
        return m.doc.Id
 
1449
}
 
1450
 
 
1451
// Placement returns the machine's Placement structure that should be used when
 
1452
// provisioning an instance for the machine.
 
1453
func (m *Machine) Placement() string {
 
1454
        return m.doc.Placement
 
1455
}
 
1456
 
 
1457
// Constraints returns the exact constraints that should apply when provisioning
 
1458
// an instance for the machine.
 
1459
func (m *Machine) Constraints() (constraints.Value, error) {
 
1460
        return readConstraints(m.st, m.globalKey())
 
1461
}
 
1462
 
 
1463
// SetConstraints sets the exact constraints to apply when provisioning an
 
1464
// instance for the machine. It will fail if the machine is Dead, or if it
 
1465
// is already provisioned.
 
1466
func (m *Machine) SetConstraints(cons constraints.Value) (err error) {
 
1467
        defer errors.DeferredAnnotatef(&err, "cannot set constraints")
 
1468
        unsupported, err := m.st.validateConstraints(cons)
 
1469
        if len(unsupported) > 0 {
 
1470
                logger.Warningf(
 
1471
                        "setting constraints on machine %q: unsupported constraints: %v", m.Id(), strings.Join(unsupported, ","))
 
1472
        } else if err != nil {
 
1473
                return err
 
1474
        }
 
1475
        notSetYet := bson.D{{"nonce", ""}}
 
1476
        ops := []txn.Op{{
 
1477
                C:      machinesC,
 
1478
                Id:     m.doc.DocID,
 
1479
                Assert: append(isAliveDoc, notSetYet...),
 
1480
        }}
 
1481
        mcons, err := m.st.resolveMachineConstraints(cons)
 
1482
        if err != nil {
 
1483
                return err
 
1484
        }
 
1485
 
 
1486
        ops = append(ops, setConstraintsOp(m.st, m.globalKey(), mcons))
 
1487
        // make multiple attempts to push the ErrExcessiveContention case out of the
 
1488
        // realm of plausibility: it implies local state indicating unprovisioned,
 
1489
        // and remote state indicating provisioned (reasonable); but which changes
 
1490
        // back to unprovisioned and then to provisioned again with *very* specific
 
1491
        // timing in the course of this loop.
 
1492
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
1493
                if attempt > 0 {
 
1494
                        if m, err = m.st.Machine(m.doc.Id); err != nil {
 
1495
                                return nil, err
 
1496
                        }
 
1497
                }
 
1498
                if m.doc.Life != Alive {
 
1499
                        return nil, errNotAlive
 
1500
                }
 
1501
                if _, err := m.InstanceId(); err == nil {
 
1502
                        return nil, fmt.Errorf("machine is already provisioned")
 
1503
                } else if !errors.IsNotProvisioned(err) {
 
1504
                        return nil, err
 
1505
                }
 
1506
                return ops, nil
 
1507
        }
 
1508
        return m.st.run(buildTxn)
 
1509
}
 
1510
 
 
1511
// Status returns the status of the machine.
 
1512
func (m *Machine) Status() (status.StatusInfo, error) {
 
1513
        mStatus, err := getStatus(m.st, m.globalKey(), "machine")
 
1514
        if err != nil {
 
1515
                return mStatus, err
 
1516
        }
 
1517
        return mStatus, nil
 
1518
}
 
1519
 
 
1520
// SetStatus sets the status of the machine.
 
1521
func (m *Machine) SetStatus(statusInfo status.StatusInfo) error {
 
1522
        switch statusInfo.Status {
 
1523
        case status.StatusStarted, status.StatusStopped:
 
1524
        case status.StatusError:
 
1525
                if statusInfo.Message == "" {
 
1526
                        return errors.Errorf("cannot set status %q without info", statusInfo.Status)
 
1527
                }
 
1528
        case status.StatusPending:
 
1529
                // If a machine is not yet provisioned, we allow its status
 
1530
                // to be set back to pending (when a retry is to occur).
 
1531
                _, err := m.InstanceId()
 
1532
                allowPending := errors.IsNotProvisioned(err)
 
1533
                if allowPending {
 
1534
                        break
 
1535
                }
 
1536
                fallthrough
 
1537
        case status.StatusDown:
 
1538
                return errors.Errorf("cannot set status %q", statusInfo.Status)
 
1539
        default:
 
1540
                return errors.Errorf("cannot set invalid status %q", statusInfo.Status)
 
1541
        }
 
1542
        return setStatus(m.st, setStatusParams{
 
1543
                badge:     "machine",
 
1544
                globalKey: m.globalKey(),
 
1545
                status:    statusInfo.Status,
 
1546
                message:   statusInfo.Message,
 
1547
                rawData:   statusInfo.Data,
 
1548
                updated:   statusInfo.Since,
 
1549
        })
 
1550
}
 
1551
 
 
1552
// StatusHistory returns a slice of at most filter.Size StatusInfo items
 
1553
// or items as old as filter.Date or items newer than now - filter.Delta time
 
1554
// representing past statuses for this machine.
 
1555
func (m *Machine) StatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) {
 
1556
        args := &statusHistoryArgs{
 
1557
                st:        m.st,
 
1558
                globalKey: m.globalKey(),
 
1559
                filter:    filter,
 
1560
        }
 
1561
        return statusHistory(args)
 
1562
}
 
1563
 
 
1564
// Clean returns true if the machine does not have any deployed units or containers.
 
1565
func (m *Machine) Clean() bool {
 
1566
        return m.doc.Clean
 
1567
}
 
1568
 
 
1569
// SupportedContainers returns any containers this machine is capable of hosting, and a bool
 
1570
// indicating if the supported containers have been determined or not.
 
1571
func (m *Machine) SupportedContainers() ([]instance.ContainerType, bool) {
 
1572
        return m.doc.SupportedContainers, m.doc.SupportedContainersKnown
 
1573
}
 
1574
 
 
1575
// SupportsNoContainers records the fact that this machine doesn't support any containers.
 
1576
func (m *Machine) SupportsNoContainers() (err error) {
 
1577
        if err = m.updateSupportedContainers([]instance.ContainerType{}); err != nil {
 
1578
                return err
 
1579
        }
 
1580
        return m.markInvalidContainers()
 
1581
}
 
1582
 
 
1583
// SetSupportedContainers sets the list of containers supported by this machine.
 
1584
func (m *Machine) SetSupportedContainers(containers []instance.ContainerType) (err error) {
 
1585
        if len(containers) == 0 {
 
1586
                return fmt.Errorf("at least one valid container type is required")
 
1587
        }
 
1588
        for _, container := range containers {
 
1589
                if container == instance.NONE {
 
1590
                        return fmt.Errorf("%q is not a valid container type", container)
 
1591
                }
 
1592
        }
 
1593
        if err = m.updateSupportedContainers(containers); err != nil {
 
1594
                return err
 
1595
        }
 
1596
        return m.markInvalidContainers()
 
1597
}
 
1598
 
 
1599
func isSupportedContainer(container instance.ContainerType, supportedContainers []instance.ContainerType) bool {
 
1600
        for _, supportedContainer := range supportedContainers {
 
1601
                if supportedContainer == container {
 
1602
                        return true
 
1603
                }
 
1604
        }
 
1605
        return false
 
1606
}
 
1607
 
 
1608
// updateSupportedContainers sets the supported containers on this host machine.
 
1609
func (m *Machine) updateSupportedContainers(supportedContainers []instance.ContainerType) (err error) {
 
1610
        ops := []txn.Op{
 
1611
                {
 
1612
                        C:      machinesC,
 
1613
                        Id:     m.doc.DocID,
 
1614
                        Assert: notDeadDoc,
 
1615
                        Update: bson.D{
 
1616
                                {"$set", bson.D{
 
1617
                                        {"supportedcontainers", supportedContainers},
 
1618
                                        {"supportedcontainersknown", true},
 
1619
                                }}},
 
1620
                },
 
1621
        }
 
1622
        if err = m.st.runTransaction(ops); err != nil {
 
1623
                err = onAbort(err, ErrDead)
 
1624
                logger.Errorf("cannot update supported containers of machine %v: %v", m, err)
 
1625
                return err
 
1626
        }
 
1627
        m.doc.SupportedContainers = supportedContainers
 
1628
        m.doc.SupportedContainersKnown = true
 
1629
        return nil
 
1630
}
 
1631
 
 
1632
// markInvalidContainers sets the status of any container belonging to this machine
 
1633
// as being in error if the container type is not supported.
 
1634
func (m *Machine) markInvalidContainers() error {
 
1635
        currentContainers, err := m.Containers()
 
1636
        if err != nil {
 
1637
                return err
 
1638
        }
 
1639
        for _, containerId := range currentContainers {
 
1640
                if !isSupportedContainer(ContainerTypeFromId(containerId), m.doc.SupportedContainers) {
 
1641
                        container, err := m.st.Machine(containerId)
 
1642
                        if err != nil {
 
1643
                                logger.Errorf("loading container %v to mark as invalid: %v", containerId, err)
 
1644
                                continue
 
1645
                        }
 
1646
                        // There should never be a circumstance where an unsupported container is started.
 
1647
                        // Nonetheless, we check and log an error if such a situation arises.
 
1648
                        statusInfo, err := container.Status()
 
1649
                        if err != nil {
 
1650
                                logger.Errorf("finding status of container %v to mark as invalid: %v", containerId, err)
 
1651
                                continue
 
1652
                        }
 
1653
                        if statusInfo.Status == status.StatusPending {
 
1654
                                containerType := ContainerTypeFromId(containerId)
 
1655
                                // TODO(perrito666) 2016-05-02 lp:1558657
 
1656
                                now := time.Now()
 
1657
                                s := status.StatusInfo{
 
1658
                                        Status:  status.StatusError,
 
1659
                                        Message: "unsupported container",
 
1660
                                        Data:    map[string]interface{}{"type": containerType},
 
1661
                                        Since:   &now,
 
1662
                                }
 
1663
                                container.SetStatus(s)
 
1664
                        } else {
 
1665
                                logger.Errorf("unsupported container %v has unexpected status %v", containerId, statusInfo.Status)
 
1666
                        }
 
1667
                }
 
1668
        }
 
1669
        return nil
 
1670
}
 
1671
 
 
1672
// SetMachineBlockDevices sets the block devices visible on the machine.
 
1673
func (m *Machine) SetMachineBlockDevices(info ...BlockDeviceInfo) error {
 
1674
        return setMachineBlockDevices(m.st, m.Id(), info)
 
1675
}
 
1676
 
 
1677
// VolumeAttachments returns the machine's volume attachments.
 
1678
func (m *Machine) VolumeAttachments() ([]VolumeAttachment, error) {
 
1679
        return m.st.MachineVolumeAttachments(m.MachineTag())
 
1680
}
 
1681
 
 
1682
// AddAction is part of the ActionReceiver interface.
 
1683
func (m *Machine) AddAction(name string, payload map[string]interface{}) (Action, error) {
 
1684
        spec, ok := actions.PredefinedActionsSpec[name]
 
1685
        if !ok {
 
1686
                return nil, errors.Errorf("cannot add action %q to a machine; only predefined actions allowed", name)
 
1687
        }
 
1688
 
 
1689
        // Reject bad payloads before attempting to insert defaults.
 
1690
        err := spec.ValidateParams(payload)
 
1691
        if err != nil {
 
1692
                return nil, err
 
1693
        }
 
1694
        payloadWithDefaults, err := spec.InsertDefaults(payload)
 
1695
        if err != nil {
 
1696
                return nil, err
 
1697
        }
 
1698
        return m.st.EnqueueAction(m.Tag(), name, payloadWithDefaults)
 
1699
}
 
1700
 
 
1701
// CancelAction is part of the ActionReceiver interface.
 
1702
func (m *Machine) CancelAction(action Action) (Action, error) {
 
1703
        return action.Finish(ActionResults{Status: ActionCancelled})
 
1704
}
 
1705
 
 
1706
// WatchActionNotifications is part of the ActionReceiver interface.
 
1707
func (m *Machine) WatchActionNotifications() StringsWatcher {
 
1708
        return m.st.watchEnqueuedActionsFilteredBy(m)
 
1709
}
 
1710
 
 
1711
// Actions is part of the ActionReceiver interface.
 
1712
func (m *Machine) Actions() ([]Action, error) {
 
1713
        return m.st.matchingActions(m)
 
1714
}
 
1715
 
 
1716
// CompletedActions is part of the ActionReceiver interface.
 
1717
func (m *Machine) CompletedActions() ([]Action, error) {
 
1718
        return m.st.matchingActionsCompleted(m)
 
1719
}
 
1720
 
 
1721
// PendingActions is part of the ActionReceiver interface.
 
1722
func (m *Machine) PendingActions() ([]Action, error) {
 
1723
        return m.st.matchingActionsPending(m)
 
1724
}
 
1725
 
 
1726
// RunningActions is part of the ActionReceiver interface.
 
1727
func (m *Machine) RunningActions() ([]Action, error) {
 
1728
        return m.st.matchingActionsRunning(m)
 
1729
}