1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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"
18
"gopkg.in/mgo.v2/bson"
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"
32
// Machine represents the state of a machine.
38
// MachineJob values define responsibilities that machines may be
39
// expected to fulfil.
49
jobNames = map[MachineJob]multiwatcher.MachineJob{
50
JobHostUnits: multiwatcher.JobHostUnits,
51
JobManageModel: multiwatcher.JobManageModel,
53
jobMigrationValue = map[MachineJob]string{
54
JobHostUnits: "host-units",
55
JobManageModel: "api-server",
59
// AllJobs returns all supported machine jobs.
60
func AllJobs() []MachineJob {
67
// ToParams returns the job as multiwatcher.MachineJob.
68
func (job MachineJob) ToParams() multiwatcher.MachineJob {
69
if jujuJob, ok := jobNames[job]; ok {
72
return multiwatcher.MachineJob(fmt.Sprintf("<unknown job %d>", int(job)))
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()
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 {
93
func (job MachineJob) String() string {
94
return string(job.ToParams())
97
// manualMachinePrefix signals as prefix of Nonce that a machine is
98
// manually provisioned.
99
const manualMachinePrefix = "manual:"
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"`
112
Tools *tools.Tools `bson:",omitempty"`
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.
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"`
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.
133
// MachineAddresses is the set of addresses obtained from the machine itself.
134
MachineAddresses []address
136
// PreferredPublicAddress is the preferred address to be used for
137
// the machine when a public address is requested.
138
PreferredPublicAddress address `bson:",omitempty"`
140
// PreferredPrivateAddress is the preferred address to be used for
141
// the machine when a private address is requested.
142
PreferredPrivateAddress address `bson:",omitempty"`
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"`
152
// StopMongoUntilVersion holds the version that must be checked to
153
// know if mongo must be stopped.
154
StopMongoUntilVersion string `bson:",omitempty"`
157
func newMachine(st *State, doc *machineDoc) *Machine {
165
func wantsVote(jobs []MachineJob, noVote bool) bool {
166
return hasJob(jobs, JobManageModel) && !noVote
169
// Id returns the machine id.
170
func (m *Machine) Id() string {
174
// Principals returns the principals for the machine.
175
func (m *Machine) Principals() []string {
176
return m.doc.Principals
179
// Series returns the operating system series running on the machine.
180
func (m *Machine) Series() string {
184
// ContainerType returns the type of container hosting this machine.
185
func (m *Machine) ContainerType() instance.ContainerType {
186
return instance.ContainerType(m.doc.ContainerType)
189
// machineGlobalKey returns the global database key for the identified machine.
190
func machineGlobalKey(id string) string {
194
// machineGlobalInstanceKey returns the global database key for the identified machine's instance.
195
func machineGlobalInstanceKey(id string) string {
196
return machineGlobalKey(id) + "#instance"
199
// globalInstanceKey returns the global database key for the machinei's instance.
200
func (m *Machine) globalInstanceKey() string {
201
return machineGlobalInstanceKey(m.doc.Id)
204
// globalKey returns the global database key for the machine.
205
func (m *Machine) globalKey() string {
206
return machineGlobalKey(m.doc.Id)
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"`
225
func hardwareCharacteristics(instData instanceData) *instance.HardwareCharacteristics {
226
return &instance.HardwareCharacteristics{
229
RootDisk: instData.RootDisk,
230
CpuCores: instData.CpuCores,
231
CpuPower: instData.CpuPower,
233
AvailabilityZone: instData.AvailZone,
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())
243
return hardwareCharacteristics(instData), nil
246
func getInstanceData(st *State, id string) (instanceData, error) {
247
instanceDataCollection, closer := st.getCollection(instanceDataC)
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)
256
return instanceData{}, fmt.Errorf("cannot get instance data for machine %v: %v", id, err)
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()
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())
275
// Life returns whether the machine is Alive, Dying or Dead.
276
func (m *Machine) Life() Life {
280
// Jobs returns the responsibilities that must be fulfilled by m's agent.
281
func (m *Machine) Jobs() []MachineJob {
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)
291
// HasVote reports whether that machine is currently a voting
292
// member of the replica set.
293
func (m *Machine) HasVote() bool {
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 {
305
Update: bson.D{{"$set", bson.D{{"hasvote", hasVote}}}},
307
if err := m.st.runTransaction(ops); err != nil {
308
return fmt.Errorf("cannot set HasVote of machine %v: %v", m, onAbort(err, ErrDead))
310
m.doc.HasVote = hasVote
314
// SetStopMongoUntilVersion sets a version that is to be checked against
315
// the agent config before deciding if mongo must be started on a
317
func (m *Machine) SetStopMongoUntilVersion(v mongo.Version) error {
321
Update: bson.D{{"$set", bson.D{{"stopmongountilversion", v.String()}}}},
323
if err := m.st.runTransaction(ops); err != nil {
324
return fmt.Errorf("cannot set StopMongoUntilVersion %v: %v", m, onAbort(err, ErrDead))
326
m.doc.StopMongoUntilVersion = v.String()
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)
336
// IsManager returns true if the machine has JobManageModel.
337
func (m *Machine) IsManager() bool {
338
return hasJob(m.doc.Jobs, JobManageModel)
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) {
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.
353
cfg, err := m.st.ModelConfig()
358
return t == "null" || t == "manual", nil
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)
370
tools := *m.doc.Tools
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")
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 {
390
tools := &tools.Tools{Version: v}
395
Update: bson.D{{"$set", bson.D{{"tools", tools}}}},
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)
407
// SetMongoPassword sets the password the agent responsible for the machine
408
// should use to communicate with the controllers. Previous passwords
410
func (m *Machine) SetMongoPassword(password string) error {
412
return errors.NotSupportedf("setting mongo password for non-controller machine %v", m)
414
return mongo.SetAdminMongoPassword(m.st.session, m.Tag().String(), password)
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))
422
return m.setPasswordHash(utils.AgentPasswordHash(password))
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 {
433
Update: bson.D{{"$set", bson.D{{"passwordhash", passwordHash}}}},
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
439
if err := m.st.runRawTransaction(ops); err != nil {
440
return fmt.Errorf("cannot set password of machine %v: %v", m, onAbort(err, ErrDead))
442
m.doc.PasswordHash = passwordHash
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
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
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)
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()
474
return errors.Trace(err)
476
if err := m.st.runTransaction(ops); err != txn.ErrAborted {
477
return errors.Trace(err)
482
var managerMachineError = errors.New("machine is required by the model")
484
func (m *Machine) forceDestroyOps() ([]txn.Op, error) {
486
return nil, errors.Trace(managerMachineError)
492
Assert: bson.D{{"jobs", bson.D{{"$nin", []MachineJob{JobManageModel}}}}},
493
}, m.st.newCleanupOp(cleanupForceDestroyedMachine, m.doc.Id)}, nil
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)
505
type HasAssignedUnitsError struct {
510
func (e *HasAssignedUnitsError) Error() string {
511
return fmt.Sprintf("machine %s has unit %q assigned", e.MachineId, e.UnitNames[0])
514
func IsHasAssignedUnitsError(err error) bool {
515
_, ok := err.(*HasAssignedUnitsError)
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)
525
var mc machineContainers
526
err := containerRefs.FindId(m.doc.DocID).One(&mc)
528
return mc.Children, nil
530
if err == mgo.ErrNotFound {
531
return nil, errors.NotFoundf("container info for machine %v", m.Id())
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 != ""
542
// IsContainer returns true if the machine is a container.
543
func (m *Machine) IsContainer() bool {
544
_, isContainer := m.ParentId()
548
type HasContainersError struct {
550
ContainerIds []string
553
func (e *HasContainersError) Error() string {
554
return fmt.Sprintf("machine %s is hosting containers %q", e.MachineId, strings.Join(e.ContainerIds, ","))
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)
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 {
569
Attachments []names.Tag
572
func (e *HasAttachmentsError) Error() string {
574
"machine %s has attachments %s",
575
e.MachineId, e.Attachments,
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)
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()
597
if len(containers) > 0 {
598
return &HasContainersError{
599
MachineId: original.doc.Id,
600
ContainerIds: containers,
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.
611
} else if m.doc.Life > life {
614
original.doc.Life = life
621
Update: bson.D{{"$set", bson.D{{"life", life}}}},
623
// noUnits asserts that the machine has no principal units.
624
noUnits := bson.DocElem{
626
{{"principals", bson.D{{"$size", 0}}}},
627
{{"principals", bson.D{{"$exists", false}}}},
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}}},
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 {
649
// Check that the life change is sane, and collect the assertions
650
// necessary to determine that it remains so.
653
if m.doc.Life != Alive {
654
return nil, jujutxn.ErrNoOperations
656
advanceAsserts = append(advanceAsserts, isAliveDoc...)
658
if m.doc.Life == Dead {
659
return nil, jujutxn.ErrNoOperations
661
advanceAsserts = append(advanceAsserts, notDeadDoc...)
663
panic(fmt.Errorf("cannot advance lifecycle to %v", life))
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)
674
return nil, fmt.Errorf("machine %s is a voting replica set member", m.doc.Id)
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.
682
var principalUnitnames []string
683
for _, principalUnit := range m.doc.Principals {
684
principalUnitnames = append(principalUnitnames, principalUnit)
685
u, err := m.st.Unit(principalUnit)
687
return nil, errors.Annotatef(err, "reading machine %s principal unit %v", m, m.doc.Principals[0])
689
svc, err := u.Application()
691
return nil, errors.Annotatef(err, "reading machine %s principal unit service %v", m, u.doc.Application)
693
if u.Life() == Alive && svc.Life() == Alive {
699
containers, err := m.Containers()
701
return nil, errors.Annotatef(err, "reading machine %s containers", m)
703
canDie = len(containers) == 0
706
checkUnits := bson.DocElem{
708
{{"principals", principalUnitnames}},
709
{{"principals", bson.D{{"$size", 0}}}},
710
{{"principals", bson.D{{"$exists", false}}}},
713
op.Assert = append(advanceAsserts, checkUnits)
714
containerCheck := txn.Op{
717
Assert: bson.D{{"$or", []bson.D{
718
{{"children", bson.D{{"$size", 0}}}},
719
{{"children", bson.D{{"$exists", false}}}},
722
return []txn.Op{op, containerCheck, cleanupOp}, nil
726
if len(m.doc.Principals) > 0 {
727
return nil, &HasAssignedUnitsError{
729
UnitNames: m.doc.Principals,
732
advanceAsserts = append(advanceAsserts, noUnits)
735
// A machine may not become Dead until it has no more
736
// attachments to inherently machine-bound storage.
737
storageAsserts, err := m.assertNoPersistentStorage()
739
return nil, errors.Trace(err)
741
advanceAsserts = append(advanceAsserts, storageAsserts...)
744
// Add the additional asserts needed for this transaction.
745
op.Assert = advanceAsserts
746
return []txn.Op{op, cleanupOp}, nil
748
if err = m.st.run(buildTxn); err == jujutxn.ErrExcessiveContention {
749
err = errors.Annotatef(err, "machine %s cannot advance lifecycle", m)
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)
763
return nil, errors.Trace(err)
769
for _, f := range m.doc.Filesystems {
770
tag := names.NewFilesystemTag(f)
771
machineBound, err := isFilesystemInherentlyMachineBound(m.st, tag)
773
return nil, errors.Trace(err)
779
if len(attachments) > 0 {
780
return nil, &HasAttachmentsError{
782
Attachments: attachments.SortedValues(),
785
if m.doc.Life == Dying {
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{
794
"$elemMatch", bson.D{{
795
"$nin", m.doc.Volumes,
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.
804
noNewFilesystems := bson.DocElem{
805
"filesystems", bson.D{{
807
"$elemMatch", bson.D{{
808
"$nin", m.doc.Filesystems,
813
return bson.D{noNewVolumes, noNewFilesystems}, nil
816
func (m *Machine) removePortsOps() ([]txn.Op, error) {
817
if m.doc.Life != Dead {
818
return nil, errors.Errorf("machine is not dead")
820
ports, err := m.AllPorts()
825
for _, p := range ports {
826
ops = append(ops, p.removeOps()...)
831
func (m *Machine) removeOps() ([]txn.Op, error) {
832
if m.doc.Life != Dead {
833
return nil, fmt.Errorf("machine is not dead")
839
Assert: txn.DocExists,
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()),
856
linkLayerDevicesOps, err := m.removeAllLinkLayerDevicesOps()
858
return nil, errors.Trace(err)
860
devicesAddressesOps, err := m.removeAllAddressesOps()
862
return nil, errors.Trace(err)
864
portsOps, err := m.removePortsOps()
866
return nil, errors.Trace(err)
868
filesystemOps, err := m.st.removeMachineFilesystemsOps(m.MachineTag())
870
return nil, errors.Trace(err)
872
volumeOps, err := m.st.removeMachineVolumesOps(m.MachineTag())
874
return nil, errors.Trace(err)
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...)
885
// Remove removes the machine from state. It will fail if the machine
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
893
buildTxn := func(attempt int) ([]txn.Op, error) {
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
901
return nil, errors.Trace(err)
904
ops, err := machine.removeOps()
906
return nil, errors.Trace(err)
910
return m.st.run(buildTxn)
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())
919
if errors.IsNotFound(err) {
922
return errors.Annotatef(err, "cannot refresh machine %v", m)
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())
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++ {
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()
954
panic(fmt.Sprintf("presence reported dead status twice in a row for machine %v", m))
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())
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
972
// TODO: Does not work for multiple controllers. Trigger a sync across all controllers.
974
m.st.workers.PresenceWatcher().Sync()
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())
989
return instData.InstanceId, err
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")
997
logger.Warningf("error when retrieving instance status for machine: %s, %v", m.Id(), err)
998
return status.StatusInfo{}, err
1000
return machineStatus, nil
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{
1007
globalKey: m.globalInstanceKey(),
1008
status: sInfo.Status,
1009
message: sInfo.Message,
1010
rawData: sInfo.Data,
1011
updated: sInfo.Since,
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{
1024
globalKey: m.globalInstanceKey(),
1027
return statusHistory(args)
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()))
1038
return "", errors.Trace(err)
1041
if instData.AvailZone != nil {
1042
zone = *instData.AvailZone
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)
1053
pudocs := []unitDoc{}
1054
err = unitsCollection.Find(bson.D{{"machineid", m.doc.Id}}).All(&pudocs)
1058
for _, pudoc := range pudocs {
1059
units = append(units, newUnit(m.st, &pudoc))
1061
err = unitsCollection.Find(bson.D{{"principal", pudoc.Name}}).All(&docs)
1065
for _, doc := range docs {
1066
units = append(units, newUnit(m.st, &doc))
1072
// SetProvisioned sets the provider specific machine id, nonce and also metadata for
1073
// this machine. Once set, the instance id cannot be changed.
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)
1083
if id == "" || nonce == "" {
1084
return fmt.Errorf("instance id and nonce cannot be empty")
1087
if characteristics == nil {
1088
characteristics = &instance.HardwareCharacteristics{}
1090
instData := &instanceData{
1092
MachineId: m.doc.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,
1108
Assert: append(isAliveDoc, bson.DocElem{"nonce", ""}),
1109
Update: bson.D{{"$set", bson.D{{"nonce", nonce}}}},
1113
Assert: txn.DocMissing,
1118
if err = m.st.runTransaction(ops); err == nil {
1121
} else if err != txn.ErrAborted {
1123
} else if alive, err := isAlive(m.st, machinesC, m.doc.DocID); err != nil {
1128
return fmt.Errorf("already set")
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,
1141
if err := m.SetParentLinkLayerDevicesBeforeTheirChildren(devicesArgs); err != nil {
1142
return errors.Trace(err)
1144
if err := m.SetDevicesAddressesIdempotently(devicesAddrs); err != nil {
1145
return errors.Trace(err)
1147
if err := setProvisionedVolumeInfo(m.st, volumes); err != nil {
1148
return errors.Trace(err)
1150
if err := setMachineVolumeAttachmentInfo(m.st, m.Id(), volumeAttachments); err != nil {
1151
return errors.Trace(err)
1153
return m.SetProvisioned(id, nonce, characteristics)
1156
// Addresses returns any hostnames and ips associated with a machine,
1157
// determined both by the machine itself, and by asking the provider.
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))
1167
func containsAddress(addresses []address, address address) bool {
1168
for _, addr := range addresses {
1169
if addr.Value == address.Value {
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()
1181
if publicAddress.Value == "" {
1182
err = network.NoAddressError("public")
1184
return publicAddress, err
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
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.
1194
netAddr := getAddr(providerAddresses)
1195
if netAddr.Value == "" {
1196
netAddr = getAddr(machineAddresses)
1197
newAddr = fromNetworkAddress(netAddr, OriginMachine)
1199
newAddr = fromNetworkAddress(netAddr, OriginProvider)
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 != ""
1211
if !containsAddress(providerAddresses, addr) && !containsAddress(machineAddresses, addr) {
1212
return newAddr, true
1214
if Origin(addr.Origin) != OriginProvider && Origin(newAddr.Origin) == OriginProvider {
1215
return newAddr, true
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)
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()
1233
if privateAddress.Value == "" {
1234
err = network.NoAddressError("private")
1236
return privateAddress, err
1239
func (m *Machine) setPreferredAddressOps(addr address, isPublic bool) []txn.Op {
1240
fieldName := "preferredprivateaddress"
1241
current := m.doc.PreferredPrivateAddress
1243
fieldName = "preferredpublicaddress"
1244
current = m.doc.PreferredPublicAddress
1246
// Assert that the field is either missing (never been set) or is
1247
// unchanged from its previous value.
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}},
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}})
1260
if current.Origin != "" {
1261
currentD = append(currentD, bson.D{{fieldName + ".origin", current.Origin}})
1263
if current.SpaceName != "" {
1264
currentD = append(currentD, bson.D{{fieldName + ".spacename", current.SpaceName}})
1267
assert := bson.D{{"$or", []bson.D{
1268
{{"$and", currentD}},
1269
{{fieldName, nil}}}}}
1274
Update: bson.D{{"$set", bson.D{{fieldName, addr}}}},
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)
1286
// Without an exact match, prefer a fallback match.
1287
getAddr := func(addresses []address) network.Address {
1288
addr, _ := network.SelectPublicAddress(networkAddresses(addresses))
1292
newAddr, changed := maybeGetNewAddress(publicAddress, providerAddresses, machineAddresses, getAddr, checkScope)
1294
// No change, so no ops.
1295
return []txn.Op{}, publicAddress, false
1298
ops := m.setPreferredAddressOps(newAddr, true)
1299
return ops, newAddr, true
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)
1308
// Without an exact match, prefer a fallback match.
1309
getAddr := func(addresses []address) network.Address {
1310
addr, _ := network.SelectInternalAddress(networkAddresses(addresses), false)
1314
newAddr, changed := maybeGetNewAddress(privateAddress, providerAddresses, machineAddresses, getAddr, checkScope)
1316
// No change, so no ops.
1317
return []txn.Op{}, privateAddress, false
1319
ops := m.setPreferredAddressOps(newAddr, false)
1320
return ops, newAddr, true
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())
1328
return errors.Annotatef(err, "cannot refresh provider addresses for machine %s", m)
1330
if err = m.setAddresses(addresses, &mdoc.Addresses, "addresses"); err != nil {
1331
return fmt.Errorf("cannot set addresses of machine %v: %v", m, err)
1333
m.doc.Addresses = mdoc.Addresses
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())
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())
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())
1360
return errors.Annotatef(err, "cannot refresh machine addresses for machine %s", m)
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)
1365
m.doc.MachineAddresses = mdoc.MachineAddresses
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)
1377
// Update addresses now.
1378
network.SortAddresses(addressesToSet)
1379
origin := OriginProvider
1380
if fieldName == "machineaddresses" {
1381
origin = OriginMachine
1383
stateAddresses := fromNetworkAddresses(addressesToSet, origin)
1386
newPrivate, newPublic address
1387
changedPrivate, changedPublic bool
1391
buildTxn := func(attempt int) ([]txn.Op, error) {
1393
if machine, err = machine.st.Machine(machine.doc.Id); err != nil {
1397
if machine.doc.Life == Dead {
1398
return nil, errNotAlive
1402
Id: machine.doc.DocID,
1404
Update: bson.D{{"$set", bson.D{{fieldName, stateAddresses}}}},
1407
var providerAddresses []address
1408
var machineAddresses []address
1409
if fieldName == "machineaddresses" {
1410
providerAddresses = machine.doc.Addresses
1411
machineAddresses = stateAddresses
1413
machineAddresses = machine.doc.MachineAddresses
1414
providerAddresses = stateAddresses
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...)
1424
err = m.st.run(buildTxn)
1425
if err == txn.ErrAborted {
1427
} else if err != nil {
1428
return errors.Trace(err)
1431
*field = stateAddresses
1433
m.doc.PreferredPrivateAddress = newPrivate
1436
m.doc.PreferredPublicAddress = newPublic
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 != ""
1446
// String returns a unique description of this machine.
1447
func (m *Machine) String() string {
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
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())
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 {
1471
"setting constraints on machine %q: unsupported constraints: %v", m.Id(), strings.Join(unsupported, ","))
1472
} else if err != nil {
1475
notSetYet := bson.D{{"nonce", ""}}
1479
Assert: append(isAliveDoc, notSetYet...),
1481
mcons, err := m.st.resolveMachineConstraints(cons)
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) {
1494
if m, err = m.st.Machine(m.doc.Id); err != nil {
1498
if m.doc.Life != Alive {
1499
return nil, errNotAlive
1501
if _, err := m.InstanceId(); err == nil {
1502
return nil, fmt.Errorf("machine is already provisioned")
1503
} else if !errors.IsNotProvisioned(err) {
1508
return m.st.run(buildTxn)
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")
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)
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)
1537
case status.StatusDown:
1538
return errors.Errorf("cannot set status %q", statusInfo.Status)
1540
return errors.Errorf("cannot set invalid status %q", statusInfo.Status)
1542
return setStatus(m.st, setStatusParams{
1544
globalKey: m.globalKey(),
1545
status: statusInfo.Status,
1546
message: statusInfo.Message,
1547
rawData: statusInfo.Data,
1548
updated: statusInfo.Since,
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{
1558
globalKey: m.globalKey(),
1561
return statusHistory(args)
1564
// Clean returns true if the machine does not have any deployed units or containers.
1565
func (m *Machine) Clean() bool {
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
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 {
1580
return m.markInvalidContainers()
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")
1588
for _, container := range containers {
1589
if container == instance.NONE {
1590
return fmt.Errorf("%q is not a valid container type", container)
1593
if err = m.updateSupportedContainers(containers); err != nil {
1596
return m.markInvalidContainers()
1599
func isSupportedContainer(container instance.ContainerType, supportedContainers []instance.ContainerType) bool {
1600
for _, supportedContainer := range supportedContainers {
1601
if supportedContainer == container {
1608
// updateSupportedContainers sets the supported containers on this host machine.
1609
func (m *Machine) updateSupportedContainers(supportedContainers []instance.ContainerType) (err error) {
1617
{"supportedcontainers", supportedContainers},
1618
{"supportedcontainersknown", true},
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)
1627
m.doc.SupportedContainers = supportedContainers
1628
m.doc.SupportedContainersKnown = true
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()
1639
for _, containerId := range currentContainers {
1640
if !isSupportedContainer(ContainerTypeFromId(containerId), m.doc.SupportedContainers) {
1641
container, err := m.st.Machine(containerId)
1643
logger.Errorf("loading container %v to mark as invalid: %v", containerId, err)
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()
1650
logger.Errorf("finding status of container %v to mark as invalid: %v", containerId, err)
1653
if statusInfo.Status == status.StatusPending {
1654
containerType := ContainerTypeFromId(containerId)
1655
// TODO(perrito666) 2016-05-02 lp:1558657
1657
s := status.StatusInfo{
1658
Status: status.StatusError,
1659
Message: "unsupported container",
1660
Data: map[string]interface{}{"type": containerType},
1663
container.SetStatus(s)
1665
logger.Errorf("unsupported container %v has unexpected status %v", containerId, statusInfo.Status)
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)
1677
// VolumeAttachments returns the machine's volume attachments.
1678
func (m *Machine) VolumeAttachments() ([]VolumeAttachment, error) {
1679
return m.st.MachineVolumeAttachments(m.MachineTag())
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]
1686
return nil, errors.Errorf("cannot add action %q to a machine; only predefined actions allowed", name)
1689
// Reject bad payloads before attempting to insert defaults.
1690
err := spec.ValidateParams(payload)
1694
payloadWithDefaults, err := spec.InsertDefaults(payload)
1698
return m.st.EnqueueAction(m.Tag(), name, payloadWithDefaults)
1701
// CancelAction is part of the ActionReceiver interface.
1702
func (m *Machine) CancelAction(action Action) (Action, error) {
1703
return action.Finish(ActionResults{Status: ActionCancelled})
1706
// WatchActionNotifications is part of the ActionReceiver interface.
1707
func (m *Machine) WatchActionNotifications() StringsWatcher {
1708
return m.st.watchEnqueuedActionsFilteredBy(m)
1711
// Actions is part of the ActionReceiver interface.
1712
func (m *Machine) Actions() ([]Action, error) {
1713
return m.st.matchingActions(m)
1716
// CompletedActions is part of the ActionReceiver interface.
1717
func (m *Machine) CompletedActions() ([]Action, error) {
1718
return m.st.matchingActionsCompleted(m)
1721
// PendingActions is part of the ActionReceiver interface.
1722
func (m *Machine) PendingActions() ([]Action, error) {
1723
return m.st.matchingActionsPending(m)
1726
// RunningActions is part of the ActionReceiver interface.
1727
func (m *Machine) RunningActions() ([]Action, error) {
1728
return m.st.matchingActionsRunning(m)