github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/machine.go (about)

     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  	"net"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/names"
    14  	jujutxn "github.com/juju/txn"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/utils/set"
    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/instance"
    23  	"github.com/juju/juju/mongo"
    24  	"github.com/juju/juju/network"
    25  	"github.com/juju/juju/state/multiwatcher"
    26  	"github.com/juju/juju/state/presence"
    27  	"github.com/juju/juju/tools"
    28  	"github.com/juju/juju/version"
    29  )
    30  
    31  // Machine represents the state of a machine.
    32  type Machine struct {
    33  	st  *State
    34  	doc machineDoc
    35  	presence.Presencer
    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  	JobManageEnviron
    46  	JobManageNetworking
    47  
    48  	// Deprecated in 1.18.
    49  	JobManageStateDeprecated
    50  )
    51  
    52  var jobNames = map[MachineJob]multiwatcher.MachineJob{
    53  	JobHostUnits:        multiwatcher.JobHostUnits,
    54  	JobManageEnviron:    multiwatcher.JobManageEnviron,
    55  	JobManageNetworking: multiwatcher.JobManageNetworking,
    56  
    57  	// Deprecated in 1.18.
    58  	JobManageStateDeprecated: multiwatcher.JobManageStateDeprecated,
    59  }
    60  
    61  // AllJobs returns all supported machine jobs.
    62  func AllJobs() []MachineJob {
    63  	return []MachineJob{
    64  		JobHostUnits,
    65  		JobManageEnviron,
    66  		JobManageNetworking,
    67  	}
    68  }
    69  
    70  // ToParams returns the job as multiwatcher.MachineJob.
    71  func (job MachineJob) ToParams() multiwatcher.MachineJob {
    72  	if jujuJob, ok := jobNames[job]; ok {
    73  		return jujuJob
    74  	}
    75  	return multiwatcher.MachineJob(fmt.Sprintf("<unknown job %d>", int(job)))
    76  }
    77  
    78  // params.JobsFromJobs converts state jobs to juju jobs.
    79  func paramsJobsFromJobs(jobs []MachineJob) []multiwatcher.MachineJob {
    80  	jujuJobs := make([]multiwatcher.MachineJob, len(jobs))
    81  	for i, machineJob := range jobs {
    82  		jujuJobs[i] = machineJob.ToParams()
    83  	}
    84  	return jujuJobs
    85  }
    86  
    87  func (job MachineJob) String() string {
    88  	return string(job.ToParams())
    89  }
    90  
    91  // manualMachinePrefix signals as prefix of Nonce that a machine is
    92  // manually provisioned.
    93  const manualMachinePrefix = "manual:"
    94  
    95  // machineDoc represents the internal state of a machine in MongoDB.
    96  // Note the correspondence with MachineInfo in apiserver/juju.
    97  type machineDoc struct {
    98  	DocID         string `bson:"_id"`
    99  	Id            string `bson:"machineid"`
   100  	EnvUUID       string `bson:"env-uuid"`
   101  	Nonce         string
   102  	Series        string
   103  	ContainerType string
   104  	Principals    []string
   105  	Life          Life
   106  	Tools         *tools.Tools `bson:",omitempty"`
   107  	Jobs          []MachineJob
   108  	NoVote        bool
   109  	HasVote       bool
   110  	PasswordHash  string
   111  	Clean         bool
   112  	// We store 2 different sets of addresses for the machine, obtained
   113  	// from different sources.
   114  	// Addresses is the set of addresses obtained by asking the provider.
   115  	Addresses []address
   116  	// MachineAddresses is the set of addresses obtained from the machine itself.
   117  	MachineAddresses []address
   118  	// The SupportedContainers attributes are used to advertise what containers this
   119  	// machine is capable of hosting.
   120  	SupportedContainersKnown bool
   121  	SupportedContainers      []instance.ContainerType `bson:",omitempty"`
   122  	// Placement is the placement directive that should be used when provisioning
   123  	// an instance for the machine.
   124  	Placement string `bson:",omitempty"`
   125  }
   126  
   127  func newMachine(st *State, doc *machineDoc) *Machine {
   128  	machine := &Machine{
   129  		st:  st,
   130  		doc: *doc,
   131  	}
   132  	return machine
   133  }
   134  
   135  func wantsVote(jobs []MachineJob, noVote bool) bool {
   136  	return hasJob(jobs, JobManageEnviron) && !noVote
   137  }
   138  
   139  // Id returns the machine id.
   140  func (m *Machine) Id() string {
   141  	return m.doc.Id
   142  }
   143  
   144  // Series returns the operating system series running on the machine.
   145  func (m *Machine) Series() string {
   146  	return m.doc.Series
   147  }
   148  
   149  // ContainerType returns the type of container hosting this machine.
   150  func (m *Machine) ContainerType() instance.ContainerType {
   151  	return instance.ContainerType(m.doc.ContainerType)
   152  }
   153  
   154  // machineGlobalKey returns the global database key for the identified machine.
   155  func machineGlobalKey(id string) string {
   156  	return "m#" + id
   157  }
   158  
   159  // globalKey returns the global database key for the machine.
   160  func (m *Machine) globalKey() string {
   161  	return machineGlobalKey(m.doc.Id)
   162  }
   163  
   164  // instanceData holds attributes relevant to a provisioned machine.
   165  type instanceData struct {
   166  	DocID      string      `bson:"_id"`
   167  	MachineId  string      `bson:"machineid"`
   168  	InstanceId instance.Id `bson:"instanceid"`
   169  	EnvUUID    string      `bson:"env-uuid"`
   170  	Status     string      `bson:"status,omitempty"`
   171  	Arch       *string     `bson:"arch,omitempty"`
   172  	Mem        *uint64     `bson:"mem,omitempty"`
   173  	RootDisk   *uint64     `bson:"rootdisk,omitempty"`
   174  	CpuCores   *uint64     `bson:"cpucores,omitempty"`
   175  	CpuPower   *uint64     `bson:"cpupower,omitempty"`
   176  	Tags       *[]string   `bson:"tags,omitempty"`
   177  	AvailZone  *string     `bson:"availzone,omitempty"`
   178  }
   179  
   180  func hardwareCharacteristics(instData instanceData) *instance.HardwareCharacteristics {
   181  	return &instance.HardwareCharacteristics{
   182  		Arch:             instData.Arch,
   183  		Mem:              instData.Mem,
   184  		RootDisk:         instData.RootDisk,
   185  		CpuCores:         instData.CpuCores,
   186  		CpuPower:         instData.CpuPower,
   187  		Tags:             instData.Tags,
   188  		AvailabilityZone: instData.AvailZone,
   189  	}
   190  }
   191  
   192  // TODO(wallyworld): move this method to a service.
   193  func (m *Machine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) {
   194  	instData, err := getInstanceData(m.st, m.Id())
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	return hardwareCharacteristics(instData), nil
   199  }
   200  
   201  func getInstanceData(st *State, id string) (instanceData, error) {
   202  	instanceDataCollection, closer := st.getCollection(instanceDataC)
   203  	defer closer()
   204  
   205  	var instData instanceData
   206  	err := instanceDataCollection.FindId(id).One(&instData)
   207  	if err == mgo.ErrNotFound {
   208  		return instanceData{}, errors.NotFoundf("instance data for machine %v", id)
   209  	}
   210  	if err != nil {
   211  		return instanceData{}, fmt.Errorf("cannot get instance data for machine %v: %v", id, err)
   212  	}
   213  	return instData, nil
   214  }
   215  
   216  // Tag returns a tag identifying the machine. The String method provides a
   217  // string representation that is safe to use as a file name. The returned name
   218  // will be different from other Tag values returned by any other entities
   219  // from the same state.
   220  func (m *Machine) Tag() names.Tag {
   221  	return m.MachineTag()
   222  }
   223  
   224  // MachineTag returns the more specific MachineTag type as opposed
   225  // to the more generic Tag type.
   226  func (m *Machine) MachineTag() names.MachineTag {
   227  	return names.NewMachineTag(m.Id())
   228  }
   229  
   230  // Life returns whether the machine is Alive, Dying or Dead.
   231  func (m *Machine) Life() Life {
   232  	return m.doc.Life
   233  }
   234  
   235  // Jobs returns the responsibilities that must be fulfilled by m's agent.
   236  func (m *Machine) Jobs() []MachineJob {
   237  	return m.doc.Jobs
   238  }
   239  
   240  // WantsVote reports whether the machine is a state server
   241  // that wants to take part in peer voting.
   242  func (m *Machine) WantsVote() bool {
   243  	return wantsVote(m.doc.Jobs, m.doc.NoVote)
   244  }
   245  
   246  // HasVote reports whether that machine is currently a voting
   247  // member of the replica set.
   248  func (m *Machine) HasVote() bool {
   249  	return m.doc.HasVote
   250  }
   251  
   252  // SetHasVote sets whether the machine is currently a voting
   253  // member of the replica set. It should only be called
   254  // from the worker that maintains the replica set.
   255  func (m *Machine) SetHasVote(hasVote bool) error {
   256  	ops := []txn.Op{{
   257  		C:      machinesC,
   258  		Id:     m.doc.DocID,
   259  		Assert: notDeadDoc,
   260  		Update: bson.D{{"$set", bson.D{{"hasvote", hasVote}}}},
   261  	}}
   262  	if err := m.st.runTransaction(ops); err != nil {
   263  		return fmt.Errorf("cannot set HasVote of machine %v: %v", m, onAbort(err, ErrDead))
   264  	}
   265  	m.doc.HasVote = hasVote
   266  	return nil
   267  }
   268  
   269  // IsManager returns true if the machine has JobManageEnviron.
   270  func (m *Machine) IsManager() bool {
   271  	return hasJob(m.doc.Jobs, JobManageEnviron)
   272  }
   273  
   274  // IsManual returns true if the machine was manually provisioned.
   275  func (m *Machine) IsManual() (bool, error) {
   276  	// Apart from the bootstrap machine, manually provisioned
   277  	// machines have a nonce prefixed with "manual:". This is
   278  	// unique to manual provisioning.
   279  	if strings.HasPrefix(m.doc.Nonce, manualMachinePrefix) {
   280  		return true, nil
   281  	}
   282  	// The bootstrap machine uses BootstrapNonce, so in that
   283  	// case we need to check if its provider type is "manual".
   284  	// We also check for "null", which is an alias for manual.
   285  	if m.doc.Id == "0" {
   286  		cfg, err := m.st.EnvironConfig()
   287  		if err != nil {
   288  			return false, err
   289  		}
   290  		t := cfg.Type()
   291  		return t == "null" || t == "manual", nil
   292  	}
   293  	return false, nil
   294  }
   295  
   296  // AgentTools returns the tools that the agent is currently running.
   297  // It returns an error that satisfies errors.IsNotFound if the tools
   298  // have not yet been set.
   299  func (m *Machine) AgentTools() (*tools.Tools, error) {
   300  	if m.doc.Tools == nil {
   301  		return nil, errors.NotFoundf("agent tools for machine %v", m)
   302  	}
   303  	tools := *m.doc.Tools
   304  	return &tools, nil
   305  }
   306  
   307  // checkVersionValidity checks whether the given version is suitable
   308  // for passing to SetAgentVersion.
   309  func checkVersionValidity(v version.Binary) error {
   310  	if v.Series == "" || v.Arch == "" {
   311  		return fmt.Errorf("empty series or arch")
   312  	}
   313  	return nil
   314  }
   315  
   316  // SetAgentVersion sets the version of juju that the agent is
   317  // currently running.
   318  func (m *Machine) SetAgentVersion(v version.Binary) (err error) {
   319  	defer errors.DeferredAnnotatef(&err, "cannot set agent version for machine %v", m)
   320  	if err = checkVersionValidity(v); err != nil {
   321  		return err
   322  	}
   323  	tools := &tools.Tools{Version: v}
   324  	ops := []txn.Op{{
   325  		C:      machinesC,
   326  		Id:     m.doc.DocID,
   327  		Assert: notDeadDoc,
   328  		Update: bson.D{{"$set", bson.D{{"tools", tools}}}},
   329  	}}
   330  	// A "raw" transaction is needed here because this function gets
   331  	// called before database migraions have run so we don't
   332  	// necessarily want the env UUID added to the id.
   333  	if err := m.st.runRawTransaction(ops); err != nil {
   334  		return onAbort(err, ErrDead)
   335  	}
   336  	m.doc.Tools = tools
   337  	return nil
   338  }
   339  
   340  // SetMongoPassword sets the password the agent responsible for the machine
   341  // should use to communicate with the state servers.  Previous passwords
   342  // are invalidated.
   343  func (m *Machine) SetMongoPassword(password string) error {
   344  	if !m.IsManager() {
   345  		return errors.NotSupportedf("setting mongo password for non-state server machine %v", m)
   346  	}
   347  	return mongo.SetAdminMongoPassword(m.st.db.Session, m.Tag().String(), password)
   348  }
   349  
   350  // SetPassword sets the password for the machine's agent.
   351  func (m *Machine) SetPassword(password string) error {
   352  	if len(password) < utils.MinAgentPasswordLength {
   353  		return fmt.Errorf("password is only %d bytes long, and is not a valid Agent password", len(password))
   354  	}
   355  	return m.setPasswordHash(utils.AgentPasswordHash(password))
   356  }
   357  
   358  // setPasswordHash sets the underlying password hash in the database directly
   359  // to the value supplied. This is split out from SetPassword to allow direct
   360  // manipulation in tests (to check for backwards compatibility).
   361  func (m *Machine) setPasswordHash(passwordHash string) error {
   362  	ops := []txn.Op{{
   363  		C:      machinesC,
   364  		Id:     m.doc.DocID,
   365  		Assert: notDeadDoc,
   366  		Update: bson.D{{"$set", bson.D{{"passwordhash", passwordHash}}}},
   367  	}}
   368  	if err := m.st.runTransaction(ops); err != nil {
   369  		return fmt.Errorf("cannot set password of machine %v: %v", m, onAbort(err, ErrDead))
   370  	}
   371  	m.doc.PasswordHash = passwordHash
   372  	return nil
   373  }
   374  
   375  // Return the underlying PasswordHash stored in the database. Used by the test
   376  // suite to check that the PasswordHash gets properly updated to new values
   377  // when compatibility mode is detected.
   378  func (m *Machine) getPasswordHash() string {
   379  	return m.doc.PasswordHash
   380  }
   381  
   382  // PasswordValid returns whether the given password is valid
   383  // for the given machine.
   384  func (m *Machine) PasswordValid(password string) bool {
   385  	agentHash := utils.AgentPasswordHash(password)
   386  	if agentHash == m.doc.PasswordHash {
   387  		return true
   388  	}
   389  	// In Juju 1.16 and older we used the slower password hash for unit
   390  	// agents. So check to see if the supplied password matches the old
   391  	// path, and if so, update it to the new mechanism.
   392  	// We ignore any error in setting the password, as we'll just try again
   393  	// next time
   394  	if utils.UserPasswordHash(password, utils.CompatSalt) == m.doc.PasswordHash {
   395  		logger.Debugf("%s logged in with old password hash, changing to AgentPasswordHash",
   396  			m.Tag())
   397  		m.setPasswordHash(agentHash)
   398  		return true
   399  	}
   400  	return false
   401  }
   402  
   403  // Destroy sets the machine lifecycle to Dying if it is Alive. It does
   404  // nothing otherwise. Destroy will fail if the machine has principal
   405  // units assigned, or if the machine has JobManageEnviron.
   406  // If the machine has assigned units, Destroy will return
   407  // a HasAssignedUnitsError.
   408  func (m *Machine) Destroy() error {
   409  	return m.advanceLifecycle(Dying)
   410  }
   411  
   412  // ForceDestroy queues the machine for complete removal, including the
   413  // destruction of all units and containers on the machine.
   414  func (m *Machine) ForceDestroy() error {
   415  	if !m.IsManager() {
   416  		ops := []txn.Op{{
   417  			C:      machinesC,
   418  			Id:     m.doc.DocID,
   419  			Assert: bson.D{{"jobs", bson.D{{"$nin", []MachineJob{JobManageEnviron}}}}},
   420  		}, m.st.newCleanupOp(cleanupForceDestroyedMachine, m.doc.Id)}
   421  		if err := m.st.runTransaction(ops); err != txn.ErrAborted {
   422  			return err
   423  		}
   424  	}
   425  	return fmt.Errorf("machine %s is required by the environment", m.doc.Id)
   426  }
   427  
   428  // EnsureDead sets the machine lifecycle to Dead if it is Alive or Dying.
   429  // It does nothing otherwise. EnsureDead will fail if the machine has
   430  // principal units assigned, or if the machine has JobManageEnviron.
   431  // If the machine has assigned units, EnsureDead will return
   432  // a HasAssignedUnitsError.
   433  func (m *Machine) EnsureDead() error {
   434  	return m.advanceLifecycle(Dead)
   435  }
   436  
   437  type HasAssignedUnitsError struct {
   438  	MachineId string
   439  	UnitNames []string
   440  }
   441  
   442  func (e *HasAssignedUnitsError) Error() string {
   443  	return fmt.Sprintf("machine %s has unit %q assigned", e.MachineId, e.UnitNames[0])
   444  }
   445  
   446  func IsHasAssignedUnitsError(err error) bool {
   447  	_, ok := err.(*HasAssignedUnitsError)
   448  	return ok
   449  }
   450  
   451  // Containers returns the container ids belonging to a parent machine.
   452  // TODO(wallyworld): move this method to a service
   453  func (m *Machine) Containers() ([]string, error) {
   454  	containerRefs, closer := m.st.getCollection(containerRefsC)
   455  	defer closer()
   456  
   457  	var mc machineContainers
   458  	err := containerRefs.FindId(m.doc.DocID).One(&mc)
   459  	if err == nil {
   460  		return mc.Children, nil
   461  	}
   462  	if err == mgo.ErrNotFound {
   463  		return nil, errors.NotFoundf("container info for machine %v", m.Id())
   464  	}
   465  	return nil, err
   466  }
   467  
   468  // ParentId returns the Id of the host machine if this machine is a container.
   469  func (m *Machine) ParentId() (string, bool) {
   470  	parentId := ParentId(m.Id())
   471  	return parentId, parentId != ""
   472  }
   473  
   474  // IsContainer returns true if the machine is a container.
   475  func (m *Machine) IsContainer() bool {
   476  	_, isContainer := m.ParentId()
   477  	return isContainer
   478  }
   479  
   480  type HasContainersError struct {
   481  	MachineId    string
   482  	ContainerIds []string
   483  }
   484  
   485  func (e *HasContainersError) Error() string {
   486  	return fmt.Sprintf("machine %s is hosting containers %q", e.MachineId, strings.Join(e.ContainerIds, ","))
   487  }
   488  
   489  func IsHasContainersError(err error) bool {
   490  	_, ok := err.(*HasContainersError)
   491  	return ok
   492  }
   493  
   494  // advanceLifecycle ensures that the machine's lifecycle is no earlier
   495  // than the supplied value. If the machine already has that lifecycle
   496  // value, or a later one, no changes will be made to remote state. If
   497  // the machine has any responsibilities that preclude a valid change in
   498  // lifecycle, it will return an error.
   499  func (original *Machine) advanceLifecycle(life Life) (err error) {
   500  	containers, err := original.Containers()
   501  	if err != nil {
   502  		return err
   503  	}
   504  	if len(containers) > 0 {
   505  		return &HasContainersError{
   506  			MachineId:    original.doc.Id,
   507  			ContainerIds: containers,
   508  		}
   509  	}
   510  	m := original
   511  	defer func() {
   512  		if err == nil {
   513  			// The machine's lifecycle is known to have advanced; it may be
   514  			// known to have already advanced further than requested, in
   515  			// which case we set the latest known valid value.
   516  			if m == nil {
   517  				life = Dead
   518  			} else if m.doc.Life > life {
   519  				life = m.doc.Life
   520  			}
   521  			original.doc.Life = life
   522  		}
   523  	}()
   524  	// op and
   525  	op := txn.Op{
   526  		C:      machinesC,
   527  		Id:     m.doc.DocID,
   528  		Update: bson.D{{"$set", bson.D{{"life", life}}}},
   529  	}
   530  	advanceAsserts := bson.D{
   531  		{"jobs", bson.D{{"$nin", []MachineJob{JobManageEnviron}}}},
   532  		{"$or", []bson.D{
   533  			{{"principals", bson.D{{"$size", 0}}}},
   534  			{{"principals", bson.D{{"$exists", false}}}},
   535  		}},
   536  		{"hasvote", bson.D{{"$ne", true}}},
   537  	}
   538  	// multiple attempts: one with original data, one with refreshed data, and a final
   539  	// one intended to determine the cause of failure of the preceding attempt.
   540  	buildTxn := func(attempt int) ([]txn.Op, error) {
   541  		// If the transaction was aborted, grab a fresh copy of the machine data.
   542  		// We don't write to original, because the expectation is that state-
   543  		// changing methods only set the requested change on the receiver; a case
   544  		// could perhaps be made that this is not a helpful convention in the
   545  		// context of the new state API, but we maintain consistency in the
   546  		// face of uncertainty.
   547  		if attempt != 0 {
   548  			if m, err = m.st.Machine(m.doc.Id); errors.IsNotFound(err) {
   549  				return nil, jujutxn.ErrNoOperations
   550  			} else if err != nil {
   551  				return nil, err
   552  			}
   553  		}
   554  		// Check that the life change is sane, and collect the assertions
   555  		// necessary to determine that it remains so.
   556  		switch life {
   557  		case Dying:
   558  			if m.doc.Life != Alive {
   559  				return nil, jujutxn.ErrNoOperations
   560  			}
   561  			op.Assert = append(advanceAsserts, isAliveDoc...)
   562  		case Dead:
   563  			if m.doc.Life == Dead {
   564  				return nil, jujutxn.ErrNoOperations
   565  			}
   566  			op.Assert = append(advanceAsserts, notDeadDoc...)
   567  		default:
   568  			panic(fmt.Errorf("cannot advance lifecycle to %v", life))
   569  		}
   570  		// Check that the machine does not have any responsibilities that
   571  		// prevent a lifecycle change.
   572  		if hasJob(m.doc.Jobs, JobManageEnviron) {
   573  			// (NOTE: When we enable multiple JobManageEnviron machines,
   574  			// this restriction will be lifted, but we will assert that the
   575  			// machine is not voting)
   576  			return nil, fmt.Errorf("machine %s is required by the environment", m.doc.Id)
   577  		}
   578  		if m.doc.HasVote {
   579  			return nil, fmt.Errorf("machine %s is a voting replica set member", m.doc.Id)
   580  		}
   581  		if len(m.doc.Principals) != 0 {
   582  			return nil, &HasAssignedUnitsError{
   583  				MachineId: m.doc.Id,
   584  				UnitNames: m.doc.Principals,
   585  			}
   586  		}
   587  		return []txn.Op{op}, nil
   588  	}
   589  	if err = m.st.run(buildTxn); err == jujutxn.ErrExcessiveContention {
   590  		err = errors.Annotatef(err, "machine %s cannot advance lifecycle", m)
   591  	}
   592  	return err
   593  }
   594  
   595  func (m *Machine) removePortsOps() ([]txn.Op, error) {
   596  	if m.doc.Life != Dead {
   597  		return nil, errors.Errorf("machine is not dead")
   598  	}
   599  	ports, err := m.AllPorts()
   600  	if err != nil {
   601  		return nil, err
   602  	}
   603  	var ops []txn.Op
   604  	for _, p := range ports {
   605  		ops = append(ops, p.removeOps()...)
   606  	}
   607  	return ops, nil
   608  }
   609  
   610  func (m *Machine) removeNetworkInterfacesOps() ([]txn.Op, error) {
   611  	if m.doc.Life != Dead {
   612  		return nil, errors.Errorf("machine is not dead")
   613  	}
   614  	ops := []txn.Op{{
   615  		C:      machinesC,
   616  		Id:     m.doc.DocID,
   617  		Assert: isDeadDoc,
   618  	}}
   619  	sel := bson.D{{"machineid", m.doc.Id}}
   620  	networkInterfaces, closer := m.st.getCollection(networkInterfacesC)
   621  	defer closer()
   622  
   623  	iter := networkInterfaces.Find(sel).Select(bson.D{{"_id", 1}}).Iter()
   624  	var doc networkInterfaceDoc
   625  	for iter.Next(&doc) {
   626  		ops = append(ops, txn.Op{
   627  			C:      networkInterfacesC,
   628  			Id:     doc.Id,
   629  			Remove: true,
   630  		})
   631  	}
   632  	return ops, iter.Close()
   633  }
   634  
   635  // Remove removes the machine from state. It will fail if the machine
   636  // is not Dead.
   637  func (m *Machine) Remove() (err error) {
   638  	defer errors.DeferredAnnotatef(&err, "cannot remove machine %s", m.doc.Id)
   639  	if m.doc.Life != Dead {
   640  		return fmt.Errorf("machine is not dead")
   641  	}
   642  	ops := []txn.Op{
   643  		{
   644  			C:      machinesC,
   645  			Id:     m.doc.DocID,
   646  			Assert: txn.DocExists,
   647  			Remove: true,
   648  		},
   649  		{
   650  			C:      machinesC,
   651  			Id:     m.doc.DocID,
   652  			Assert: isDeadDoc,
   653  		},
   654  		{
   655  			C:      instanceDataC,
   656  			Id:     m.doc.DocID,
   657  			Remove: true,
   658  		},
   659  		removeStatusOp(m.st, m.globalKey()),
   660  		removeConstraintsOp(m.st, m.globalKey()),
   661  		removeRequestedNetworksOp(m.st, m.globalKey()),
   662  		annotationRemoveOp(m.st, m.globalKey()),
   663  		removeRebootDocOp(m.st, m.globalKey()),
   664  	}
   665  	ifacesOps, err := m.removeNetworkInterfacesOps()
   666  	if err != nil {
   667  		return err
   668  	}
   669  	portsOps, err := m.removePortsOps()
   670  	if err != nil {
   671  		return err
   672  	}
   673  	blockDeviceOps, err := removeMachineBlockDevicesOps(m.st, m.Id())
   674  	if err != nil {
   675  		return err
   676  	}
   677  	ops = append(ops, ifacesOps...)
   678  	ops = append(ops, portsOps...)
   679  	ops = append(ops, blockDeviceOps...)
   680  	ops = append(ops, removeContainerRefOps(m.st, m.Id())...)
   681  	// The only abort conditions in play indicate that the machine has already
   682  	// been removed.
   683  	return onAbort(m.st.runTransaction(ops), nil)
   684  }
   685  
   686  // Refresh refreshes the contents of the machine from the underlying
   687  // state. It returns an error that satisfies errors.IsNotFound if the
   688  // machine has been removed.
   689  func (m *Machine) Refresh() error {
   690  	machines, closer := m.st.getCollection(machinesC)
   691  	defer closer()
   692  
   693  	var doc machineDoc
   694  	err := machines.FindId(m.doc.DocID).One(&doc)
   695  	if err == mgo.ErrNotFound {
   696  		return errors.NotFoundf("machine %v", m)
   697  	}
   698  	if err != nil {
   699  		return errors.Annotatef(err, "cannot refresh machine %v", m)
   700  	}
   701  	m.doc = doc
   702  	return nil
   703  }
   704  
   705  // AgentPresence returns whether the respective remote agent is alive.
   706  func (m *Machine) AgentPresence() (bool, error) {
   707  	b, err := m.st.pwatcher.Alive(m.globalKey())
   708  	return b, err
   709  }
   710  
   711  // WaitAgentPresence blocks until the respective agent is alive.
   712  func (m *Machine) WaitAgentPresence(timeout time.Duration) (err error) {
   713  	defer errors.DeferredAnnotatef(&err, "waiting for agent of machine %v", m)
   714  	ch := make(chan presence.Change)
   715  	m.st.pwatcher.Watch(m.globalKey(), ch)
   716  	defer m.st.pwatcher.Unwatch(m.globalKey(), ch)
   717  	for i := 0; i < 2; i++ {
   718  		select {
   719  		case change := <-ch:
   720  			if change.Alive {
   721  				return nil
   722  			}
   723  		case <-time.After(timeout):
   724  			return fmt.Errorf("still not alive after timeout")
   725  		case <-m.st.pwatcher.Dead():
   726  			return m.st.pwatcher.Err()
   727  		}
   728  	}
   729  	panic(fmt.Sprintf("presence reported dead status twice in a row for machine %v", m))
   730  }
   731  
   732  // SetAgentPresence signals that the agent for machine m is alive.
   733  // It returns the started pinger.
   734  func (m *Machine) SetAgentPresence() (*presence.Pinger, error) {
   735  	presenceCollection := m.st.getPresence()
   736  	p := presence.NewPinger(presenceCollection, m.st.environTag, m.globalKey())
   737  	err := p.Start()
   738  	if err != nil {
   739  		return nil, err
   740  	}
   741  	// We preform a manual sync here so that the
   742  	// presence pinger has the most up-to-date information when it
   743  	// starts. This ensures that commands run immediately after bootstrap
   744  	// like status or ensure-availability will have an accurate values
   745  	// for agent-state.
   746  	//
   747  	// TODO: Does not work for multiple state servers. Trigger a sync across all state servers.
   748  	if m.IsManager() {
   749  		m.st.pwatcher.Sync()
   750  	}
   751  	return p, nil
   752  }
   753  
   754  // InstanceId returns the provider specific instance id for this
   755  // machine, or a NotProvisionedError, if not set.
   756  func (m *Machine) InstanceId() (instance.Id, error) {
   757  	instData, err := getInstanceData(m.st, m.Id())
   758  	if errors.IsNotFound(err) {
   759  		err = errors.NotProvisionedf("machine %v", m.Id())
   760  	}
   761  	if err != nil {
   762  		return "", err
   763  	}
   764  	return instData.InstanceId, err
   765  }
   766  
   767  // InstanceStatus returns the provider specific instance status for this machine,
   768  // or a NotProvisionedError if instance is not yet provisioned.
   769  func (m *Machine) InstanceStatus() (string, error) {
   770  	instData, err := getInstanceData(m.st, m.Id())
   771  	if errors.IsNotFound(err) {
   772  		err = errors.NotProvisionedf("machine %v", m.Id())
   773  	}
   774  	if err != nil {
   775  		return "", err
   776  	}
   777  	return instData.Status, err
   778  }
   779  
   780  // SetInstanceStatus sets the provider specific instance status for a machine.
   781  func (m *Machine) SetInstanceStatus(status string) (err error) {
   782  	defer errors.DeferredAnnotatef(&err, "cannot set instance status for machine %q", m)
   783  
   784  	ops := []txn.Op{
   785  		{
   786  			C:      instanceDataC,
   787  			Id:     m.doc.DocID,
   788  			Assert: txn.DocExists,
   789  			Update: bson.D{{"$set", bson.D{{"status", status}}}},
   790  		},
   791  	}
   792  
   793  	if err = m.st.runTransaction(ops); err == nil {
   794  		return nil
   795  	} else if err != txn.ErrAborted {
   796  		return err
   797  	}
   798  	return errors.NotProvisionedf("machine %v", m.Id())
   799  }
   800  
   801  // AvailabilityZone returns the provier-specific instance availability
   802  // zone in which the machine was provisioned.
   803  func (m *Machine) AvailabilityZone() (string, error) {
   804  	instData, err := getInstanceData(m.st, m.Id())
   805  	if errors.IsNotFound(err) {
   806  		return "", errors.Trace(errors.NotProvisionedf("machine %v", m.Id()))
   807  	}
   808  	if err != nil {
   809  		return "", errors.Trace(err)
   810  	}
   811  	var zone string
   812  	if instData.AvailZone != nil {
   813  		zone = *instData.AvailZone
   814  	}
   815  	return zone, nil
   816  }
   817  
   818  // Units returns all the units that have been assigned to the machine.
   819  func (m *Machine) Units() (units []*Unit, err error) {
   820  	defer errors.DeferredAnnotatef(&err, "cannot get units assigned to machine %v", m)
   821  	unitsCollection, closer := m.st.getCollection(unitsC)
   822  	defer closer()
   823  
   824  	pudocs := []unitDoc{}
   825  	err = unitsCollection.Find(bson.D{{"machineid", m.doc.Id}}).All(&pudocs)
   826  	if err != nil {
   827  		return nil, err
   828  	}
   829  	for _, pudoc := range pudocs {
   830  		units = append(units, newUnit(m.st, &pudoc))
   831  		docs := []unitDoc{}
   832  		err = unitsCollection.Find(bson.D{{"principal", pudoc.Name}}).All(&docs)
   833  		if err != nil {
   834  			return nil, err
   835  		}
   836  		for _, doc := range docs {
   837  			units = append(units, newUnit(m.st, &doc))
   838  		}
   839  	}
   840  	return units, nil
   841  }
   842  
   843  // SetProvisioned sets the provider specific machine id, nonce and also metadata for
   844  // this machine. Once set, the instance id cannot be changed.
   845  //
   846  // When provisioning an instance, a nonce should be created and passed
   847  // when starting it, before adding the machine to the state. This means
   848  // that if the provisioner crashes (or its connection to the state is
   849  // lost) after starting the instance, we can be sure that only a single
   850  // instance will be able to act for that machine.
   851  func (m *Machine) SetProvisioned(id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics) (err error) {
   852  	defer errors.DeferredAnnotatef(&err, "cannot set instance data for machine %q", m)
   853  
   854  	if id == "" || nonce == "" {
   855  		return fmt.Errorf("instance id and nonce cannot be empty")
   856  	}
   857  
   858  	if characteristics == nil {
   859  		characteristics = &instance.HardwareCharacteristics{}
   860  	}
   861  	instData := &instanceData{
   862  		DocID:      m.doc.DocID,
   863  		MachineId:  m.doc.Id,
   864  		InstanceId: id,
   865  		EnvUUID:    m.doc.EnvUUID,
   866  		Arch:       characteristics.Arch,
   867  		Mem:        characteristics.Mem,
   868  		RootDisk:   characteristics.RootDisk,
   869  		CpuCores:   characteristics.CpuCores,
   870  		CpuPower:   characteristics.CpuPower,
   871  		Tags:       characteristics.Tags,
   872  		AvailZone:  characteristics.AvailabilityZone,
   873  	}
   874  
   875  	ops := []txn.Op{
   876  		{
   877  			C:      machinesC,
   878  			Id:     m.doc.DocID,
   879  			Assert: append(isAliveDoc, bson.DocElem{"nonce", ""}),
   880  			Update: bson.D{{"$set", bson.D{{"nonce", nonce}}}},
   881  		}, {
   882  			C:      instanceDataC,
   883  			Id:     m.doc.DocID,
   884  			Assert: txn.DocMissing,
   885  			Insert: instData,
   886  		},
   887  	}
   888  
   889  	if err = m.st.runTransaction(ops); err == nil {
   890  		m.doc.Nonce = nonce
   891  		return nil
   892  	} else if err != txn.ErrAborted {
   893  		return err
   894  	} else if alive, err := isAlive(m.st, machinesC, m.doc.DocID); err != nil {
   895  		return err
   896  	} else if !alive {
   897  		return errNotAlive
   898  	}
   899  	return fmt.Errorf("already set")
   900  }
   901  
   902  // SetInstanceInfo is used to provision a machine and in one steps set
   903  // it's instance id, nonce, hardware characteristics, add networks and
   904  // network interfaces as needed.
   905  //
   906  // TODO(dimitern) Do all the operations described in a single
   907  // transaction, rather than using separate calls. Alternatively,
   908  // we can add all the things to create/set in a document in some
   909  // collection and have a worker that takes care of the actual work.
   910  // Merge SetProvisioned() in here or drop it at that point.
   911  func (m *Machine) SetInstanceInfo(
   912  	id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics,
   913  	networks []NetworkInfo, interfaces []NetworkInterfaceInfo,
   914  	blockDevices map[string]BlockDeviceInfo) error {
   915  
   916  	// Add the networks and interfaces first.
   917  	for _, network := range networks {
   918  		_, err := m.st.AddNetwork(network)
   919  		if err != nil && errors.IsAlreadyExists(err) {
   920  			// Ignore already existing networks.
   921  			continue
   922  		} else if err != nil {
   923  			return errors.Trace(err)
   924  		}
   925  	}
   926  	for _, iface := range interfaces {
   927  		_, err := m.AddNetworkInterface(iface)
   928  		if err != nil && errors.IsAlreadyExists(err) {
   929  			// Ignore already existing network interfaces.
   930  			continue
   931  		} else if err != nil {
   932  			return errors.Trace(err)
   933  		}
   934  	}
   935  	if err := setProvisionedBlockDeviceInfo(m.st, m.Id(), blockDevices); err != nil {
   936  		return errors.Trace(err)
   937  	}
   938  	return m.SetProvisioned(id, nonce, characteristics)
   939  }
   940  
   941  func mergedAddresses(machineAddresses, providerAddresses []address) []network.Address {
   942  	merged := make([]network.Address, 0, len(providerAddresses)+len(machineAddresses))
   943  	providerValues := set.NewStrings()
   944  	for _, address := range providerAddresses {
   945  		// Older versions of Juju may have stored an empty address so ignore it here.
   946  		if address.Value == "" || providerValues.Contains(address.Value) {
   947  			continue
   948  		}
   949  		providerValues.Add(address.Value)
   950  		merged = append(merged, address.InstanceAddress())
   951  	}
   952  	for _, address := range machineAddresses {
   953  		if !providerValues.Contains(address.Value) {
   954  			merged = append(merged, address.InstanceAddress())
   955  		}
   956  	}
   957  	return merged
   958  }
   959  
   960  // Addresses returns any hostnames and ips associated with a machine,
   961  // determined both by the machine itself, and by asking the provider.
   962  //
   963  // The addresses returned by the provider shadow any of the addresses
   964  // that the machine reported with the same address value.
   965  // Provider-reported addresses always come before machine-reported
   966  // addresses. Duplicates are removed.
   967  func (m *Machine) Addresses() (addresses []network.Address) {
   968  	return mergedAddresses(m.doc.MachineAddresses, m.doc.Addresses)
   969  }
   970  
   971  // SetAddresses records any addresses related to the machine, sourced
   972  // by asking the provider.
   973  func (m *Machine) SetAddresses(addresses ...network.Address) (err error) {
   974  	if err = m.setAddresses(addresses, &m.doc.Addresses, "addresses"); err != nil {
   975  		return fmt.Errorf("cannot set addresses of machine %v: %v", m, err)
   976  	}
   977  	return nil
   978  }
   979  
   980  // MachineAddresses returns any hostnames and ips associated with a machine,
   981  // determined by asking the machine itself.
   982  func (m *Machine) MachineAddresses() (addresses []network.Address) {
   983  	for _, address := range m.doc.MachineAddresses {
   984  		addresses = append(addresses, address.InstanceAddress())
   985  	}
   986  	return
   987  }
   988  
   989  // SetMachineAddresses records any addresses related to the machine, sourced
   990  // by asking the machine.
   991  func (m *Machine) SetMachineAddresses(addresses ...network.Address) (err error) {
   992  	if err = m.setAddresses(addresses, &m.doc.MachineAddresses, "machineaddresses"); err != nil {
   993  		return fmt.Errorf("cannot set machine addresses of machine %v: %v", m, err)
   994  	}
   995  	return nil
   996  }
   997  
   998  // setAddresses updates the machine's addresses (either Addresses or
   999  // MachineAddresses, depending on the field argument).
  1000  func (m *Machine) setAddresses(addresses []network.Address, field *[]address, fieldName string) error {
  1001  	var addressesToSet []network.Address
  1002  	if !m.IsContainer() {
  1003  		// Check addresses first. We'll only add those addresses
  1004  		// which are not in the IP address collection.
  1005  		ipAddresses, closer := m.st.getCollection(ipaddressesC)
  1006  		defer closer()
  1007  
  1008  		addressValues := make([]string, len(addresses))
  1009  		for i, address := range addresses {
  1010  			addressValues[i] = address.Value
  1011  		}
  1012  		ipDocs := []ipaddressDoc{}
  1013  		sel := bson.D{{"value", bson.D{{"$in", addressValues}}}, {"state", AddressStateAllocated}}
  1014  		err := ipAddresses.Find(sel).All(&ipDocs)
  1015  		if err != nil {
  1016  			return err
  1017  		}
  1018  		ipDocValues := set.NewStrings()
  1019  		for _, ipDoc := range ipDocs {
  1020  			ipDocValues.Add(ipDoc.Value)
  1021  		}
  1022  		for _, address := range addresses {
  1023  			if !ipDocValues.Contains(address.Value) {
  1024  				addressesToSet = append(addressesToSet, address)
  1025  			}
  1026  		}
  1027  	} else {
  1028  		// Containers will set all addresses.
  1029  		addressesToSet = make([]network.Address, len(addresses))
  1030  		copy(addressesToSet, addresses)
  1031  	}
  1032  	// Update addresses now.
  1033  	var changed bool
  1034  	envConfig, err := m.st.EnvironConfig()
  1035  	if err != nil {
  1036  		return err
  1037  	}
  1038  
  1039  	network.SortAddresses(addressesToSet, envConfig.PreferIPv6())
  1040  	stateAddresses := instanceAddressesToAddresses(addressesToSet)
  1041  	buildTxn := func(attempt int) ([]txn.Op, error) {
  1042  		changed = false
  1043  		if attempt > 0 {
  1044  			if err := m.Refresh(); err != nil {
  1045  				return nil, err
  1046  			}
  1047  		}
  1048  		if m.doc.Life == Dead {
  1049  			return nil, ErrDead
  1050  		}
  1051  		op := txn.Op{
  1052  			C:      machinesC,
  1053  			Id:     m.doc.DocID,
  1054  			Assert: append(bson.D{{fieldName, *field}}, notDeadDoc...),
  1055  		}
  1056  		if !addressesEqual(addressesToSet, addressesToInstanceAddresses(*field)) {
  1057  			op.Update = bson.D{{"$set", bson.D{{fieldName, stateAddresses}}}}
  1058  			changed = true
  1059  		}
  1060  		return []txn.Op{op}, nil
  1061  	}
  1062  	switch err := m.st.run(buildTxn); err {
  1063  	case nil:
  1064  	case jujutxn.ErrExcessiveContention:
  1065  		return errors.Annotatef(err, "cannot set %s for machine %s", fieldName, m)
  1066  	default:
  1067  		return err
  1068  	}
  1069  	if !changed {
  1070  		return nil
  1071  	}
  1072  	*field = stateAddresses
  1073  	return nil
  1074  }
  1075  
  1076  // RequestedNetworks returns the list of network names the machine
  1077  // should be on. Unlike networks specified with constraints, these
  1078  // networks are required to be present on the machine.
  1079  func (m *Machine) RequestedNetworks() ([]string, error) {
  1080  	return readRequestedNetworks(m.st, m.globalKey())
  1081  }
  1082  
  1083  // Networks returns the list of configured networks on the machine.
  1084  // The configured and requested networks on a machine must match.
  1085  func (m *Machine) Networks() ([]*Network, error) {
  1086  	requestedNetworks, err := m.RequestedNetworks()
  1087  	if err != nil {
  1088  		return nil, err
  1089  	}
  1090  	docs := []networkDoc{}
  1091  
  1092  	networksCollection, closer := m.st.getCollection(networksC)
  1093  	defer closer()
  1094  
  1095  	sel := bson.D{{"name", bson.D{{"$in", requestedNetworks}}}}
  1096  	err = networksCollection.Find(sel).All(&docs)
  1097  	if err != nil {
  1098  		return nil, err
  1099  	}
  1100  	networks := make([]*Network, len(docs))
  1101  	for i, doc := range docs {
  1102  		networks[i] = newNetwork(m.st, &doc)
  1103  	}
  1104  	return networks, nil
  1105  }
  1106  
  1107  // NetworkInterfaces returns the list of configured network interfaces
  1108  // of the machine.
  1109  func (m *Machine) NetworkInterfaces() ([]*NetworkInterface, error) {
  1110  	networkInterfaces, closer := m.st.getCollection(networkInterfacesC)
  1111  	defer closer()
  1112  
  1113  	docs := []networkInterfaceDoc{}
  1114  	err := networkInterfaces.Find(bson.D{{"machineid", m.doc.Id}}).All(&docs)
  1115  	if err != nil {
  1116  		return nil, err
  1117  	}
  1118  	ifaces := make([]*NetworkInterface, len(docs))
  1119  	for i, doc := range docs {
  1120  		ifaces[i] = newNetworkInterface(m.st, &doc)
  1121  	}
  1122  	return ifaces, nil
  1123  }
  1124  
  1125  // AddNetworkInterface creates a new network interface with the given
  1126  // args for this machine. The machine must be alive and not yet
  1127  // provisioned, and there must be no other interface with the same MAC
  1128  // address on the same network, or the same name on that machine for
  1129  // this to succeed. If a network interface already exists, the
  1130  // returned error satisfies errors.IsAlreadyExists.
  1131  func (m *Machine) AddNetworkInterface(args NetworkInterfaceInfo) (iface *NetworkInterface, err error) {
  1132  	defer errors.DeferredAnnotatef(&err, "cannot add network interface %q to machine %q", args.InterfaceName, m.doc.Id)
  1133  
  1134  	if args.MACAddress == "" {
  1135  		return nil, fmt.Errorf("MAC address must be not empty")
  1136  	}
  1137  	if _, err = net.ParseMAC(args.MACAddress); err != nil {
  1138  		return nil, err
  1139  	}
  1140  	if args.InterfaceName == "" {
  1141  		return nil, fmt.Errorf("interface name must be not empty")
  1142  	}
  1143  	doc := newNetworkInterfaceDoc(m.doc.Id, m.st.EnvironUUID(), args)
  1144  	ops := []txn.Op{{
  1145  		C:      networksC,
  1146  		Id:     m.st.docID(args.NetworkName),
  1147  		Assert: txn.DocExists,
  1148  	}, {
  1149  		C:      machinesC,
  1150  		Id:     m.doc.DocID,
  1151  		Assert: isAliveDoc,
  1152  	}, {
  1153  		C:      networkInterfacesC,
  1154  		Id:     doc.Id,
  1155  		Assert: txn.DocMissing,
  1156  		Insert: doc,
  1157  	}}
  1158  
  1159  	err = m.st.runTransaction(ops)
  1160  	switch err {
  1161  	case txn.ErrAborted:
  1162  		if _, err = m.st.Network(args.NetworkName); err != nil {
  1163  			return nil, err
  1164  		}
  1165  		if err = m.Refresh(); err != nil {
  1166  			return nil, err
  1167  		} else if m.doc.Life != Alive {
  1168  			return nil, fmt.Errorf("machine is not alive")
  1169  		}
  1170  		// Should never happen.
  1171  		logger.Errorf("unhandled assert while adding network interface doc %#v", doc)
  1172  	case nil:
  1173  		// We have a unique key restrictions on the following fields:
  1174  		// - InterfaceName, MachineId
  1175  		// - MACAddress, NetworkName
  1176  		// These will cause the insert to fail if there is another record
  1177  		// with the same combination of values in the table.
  1178  		// The txn logic does not report insertion errors, so we check
  1179  		// that the record has actually been inserted correctly before
  1180  		// reporting success.
  1181  		networkInterfaces, closer := m.st.getCollection(networkInterfacesC)
  1182  		defer closer()
  1183  
  1184  		if err = networkInterfaces.FindId(doc.Id).One(&doc); err == nil {
  1185  			return newNetworkInterface(m.st, doc), nil
  1186  		}
  1187  		sel := bson.D{{"interfacename", args.InterfaceName}, {"machineid", m.doc.Id}}
  1188  		if err = networkInterfaces.Find(sel).One(nil); err == nil {
  1189  			return nil, errors.AlreadyExistsf("%q on machine %q", args.InterfaceName, m.doc.Id)
  1190  		}
  1191  		sel = bson.D{{"macaddress", args.MACAddress}, {"networkname", args.NetworkName}}
  1192  		if err = networkInterfaces.Find(sel).One(nil); err == nil {
  1193  			return nil, errors.AlreadyExistsf("MAC address %q on network %q", args.MACAddress, args.NetworkName)
  1194  		}
  1195  		// Should never happen.
  1196  		logger.Errorf("unknown error while adding network interface doc %#v", doc)
  1197  	}
  1198  	return nil, err
  1199  }
  1200  
  1201  // CheckProvisioned returns true if the machine was provisioned with the given nonce.
  1202  func (m *Machine) CheckProvisioned(nonce string) bool {
  1203  	return nonce == m.doc.Nonce && nonce != ""
  1204  }
  1205  
  1206  // String returns a unique description of this machine.
  1207  func (m *Machine) String() string {
  1208  	return m.doc.Id
  1209  }
  1210  
  1211  // Placement returns the machine's Placement structure that should be used when
  1212  // provisioning an instance for the machine.
  1213  func (m *Machine) Placement() string {
  1214  	return m.doc.Placement
  1215  }
  1216  
  1217  // Constraints returns the exact constraints that should apply when provisioning
  1218  // an instance for the machine.
  1219  func (m *Machine) Constraints() (constraints.Value, error) {
  1220  	return readConstraints(m.st, m.globalKey())
  1221  }
  1222  
  1223  // SetConstraints sets the exact constraints to apply when provisioning an
  1224  // instance for the machine. It will fail if the machine is Dead, or if it
  1225  // is already provisioned.
  1226  func (m *Machine) SetConstraints(cons constraints.Value) (err error) {
  1227  	defer errors.DeferredAnnotatef(&err, "cannot set constraints")
  1228  	unsupported, err := m.st.validateConstraints(cons)
  1229  	if len(unsupported) > 0 {
  1230  		logger.Warningf(
  1231  			"setting constraints on machine %q: unsupported constraints: %v", m.Id(), strings.Join(unsupported, ","))
  1232  	} else if err != nil {
  1233  		return err
  1234  	}
  1235  	notSetYet := bson.D{{"nonce", ""}}
  1236  	ops := []txn.Op{
  1237  		{
  1238  			C:      machinesC,
  1239  			Id:     m.doc.DocID,
  1240  			Assert: append(isAliveDoc, notSetYet...),
  1241  		},
  1242  		setConstraintsOp(m.st, m.globalKey(), cons),
  1243  	}
  1244  	// make multiple attempts to push the ErrExcessiveContention case out of the
  1245  	// realm of plausibility: it implies local state indicating unprovisioned,
  1246  	// and remote state indicating provisioned (reasonable); but which changes
  1247  	// back to unprovisioned and then to provisioned again with *very* specific
  1248  	// timing in the course of this loop.
  1249  	buildTxn := func(attempt int) ([]txn.Op, error) {
  1250  		if attempt > 0 {
  1251  			if m, err = m.st.Machine(m.doc.Id); err != nil {
  1252  				return nil, err
  1253  			}
  1254  		}
  1255  		if m.doc.Life != Alive {
  1256  			return nil, errNotAlive
  1257  		}
  1258  		if _, err := m.InstanceId(); err == nil {
  1259  			return nil, fmt.Errorf("machine is already provisioned")
  1260  		} else if !errors.IsNotProvisioned(err) {
  1261  			return nil, err
  1262  		}
  1263  		return ops, nil
  1264  	}
  1265  	return m.st.run(buildTxn)
  1266  }
  1267  
  1268  // Status returns the status of the machine.
  1269  func (m *Machine) Status() (status Status, info string, data map[string]interface{}, err error) {
  1270  	doc, err := getStatus(m.st, m.globalKey())
  1271  	if err != nil {
  1272  		return "", "", nil, err
  1273  	}
  1274  	status = doc.Status
  1275  	info = doc.StatusInfo
  1276  	data = doc.StatusData
  1277  	return
  1278  }
  1279  
  1280  // SetStatus sets the status of the machine.
  1281  func (m *Machine) SetStatus(status Status, info string, data map[string]interface{}) error {
  1282  	// If a machine is not yet provisioned, we allow its status
  1283  	// to be set back to pending (when a retry is to occur).
  1284  	_, err := m.InstanceId()
  1285  	allowPending := errors.IsNotProvisioned(err)
  1286  	doc, err := newMachineStatusDoc(status, info, data, allowPending)
  1287  	if err != nil {
  1288  		return err
  1289  	}
  1290  	ops := []txn.Op{{
  1291  		C:      machinesC,
  1292  		Id:     m.doc.DocID,
  1293  		Assert: notDeadDoc,
  1294  	},
  1295  		updateStatusOp(m.st, m.globalKey(), doc.statusDoc),
  1296  	}
  1297  	if err = m.st.runTransaction(ops); err != nil {
  1298  		return fmt.Errorf("cannot set status of machine %q: %v", m, onAbort(err, errNotAlive))
  1299  	}
  1300  	return nil
  1301  }
  1302  
  1303  // Clean returns true if the machine does not have any deployed units or containers.
  1304  func (m *Machine) Clean() bool {
  1305  	return m.doc.Clean
  1306  }
  1307  
  1308  // SupportedContainers returns any containers this machine is capable of hosting, and a bool
  1309  // indicating if the supported containers have been determined or not.
  1310  func (m *Machine) SupportedContainers() ([]instance.ContainerType, bool) {
  1311  	return m.doc.SupportedContainers, m.doc.SupportedContainersKnown
  1312  }
  1313  
  1314  // SupportsNoContainers records the fact that this machine doesn't support any containers.
  1315  func (m *Machine) SupportsNoContainers() (err error) {
  1316  	if err = m.updateSupportedContainers([]instance.ContainerType{}); err != nil {
  1317  		return err
  1318  	}
  1319  	return m.markInvalidContainers()
  1320  }
  1321  
  1322  // SetSupportedContainers sets the list of containers supported by this machine.
  1323  func (m *Machine) SetSupportedContainers(containers []instance.ContainerType) (err error) {
  1324  	if len(containers) == 0 {
  1325  		return fmt.Errorf("at least one valid container type is required")
  1326  	}
  1327  	for _, container := range containers {
  1328  		if container == instance.NONE {
  1329  			return fmt.Errorf("%q is not a valid container type", container)
  1330  		}
  1331  	}
  1332  	if err = m.updateSupportedContainers(containers); err != nil {
  1333  		return err
  1334  	}
  1335  	return m.markInvalidContainers()
  1336  }
  1337  
  1338  func isSupportedContainer(container instance.ContainerType, supportedContainers []instance.ContainerType) bool {
  1339  	for _, supportedContainer := range supportedContainers {
  1340  		if supportedContainer == container {
  1341  			return true
  1342  		}
  1343  	}
  1344  	return false
  1345  }
  1346  
  1347  // updateSupportedContainers sets the supported containers on this host machine.
  1348  func (m *Machine) updateSupportedContainers(supportedContainers []instance.ContainerType) (err error) {
  1349  	ops := []txn.Op{
  1350  		{
  1351  			C:      machinesC,
  1352  			Id:     m.doc.DocID,
  1353  			Assert: notDeadDoc,
  1354  			Update: bson.D{
  1355  				{"$set", bson.D{
  1356  					{"supportedcontainers", supportedContainers},
  1357  					{"supportedcontainersknown", true},
  1358  				}}},
  1359  		},
  1360  	}
  1361  	if err = m.st.runTransaction(ops); err != nil {
  1362  		err = onAbort(err, ErrDead)
  1363  		logger.Errorf("cannot update supported containers of machine %v: %v", m, err)
  1364  		return err
  1365  	}
  1366  	m.doc.SupportedContainers = supportedContainers
  1367  	m.doc.SupportedContainersKnown = true
  1368  	return nil
  1369  }
  1370  
  1371  // markInvalidContainers sets the status of any container belonging to this machine
  1372  // as being in error if the container type is not supported.
  1373  func (m *Machine) markInvalidContainers() error {
  1374  	currentContainers, err := m.Containers()
  1375  	if err != nil {
  1376  		return err
  1377  	}
  1378  	for _, containerId := range currentContainers {
  1379  		if !isSupportedContainer(ContainerTypeFromId(containerId), m.doc.SupportedContainers) {
  1380  			container, err := m.st.Machine(containerId)
  1381  			if err != nil {
  1382  				logger.Errorf("loading container %v to mark as invalid: %v", containerId, err)
  1383  				continue
  1384  			}
  1385  			// There should never be a circumstance where an unsupported container is started.
  1386  			// Nonetheless, we check and log an error if such a situation arises.
  1387  			status, _, _, err := container.Status()
  1388  			if err != nil {
  1389  				logger.Errorf("finding status of container %v to mark as invalid: %v", containerId, err)
  1390  				continue
  1391  			}
  1392  			if status == StatusPending {
  1393  				containerType := ContainerTypeFromId(containerId)
  1394  				container.SetStatus(
  1395  					StatusError, "unsupported container", map[string]interface{}{"type": containerType})
  1396  			} else {
  1397  				logger.Errorf("unsupported container %v has unexpected status %v", containerId, status)
  1398  			}
  1399  		}
  1400  	}
  1401  	return nil
  1402  }
  1403  
  1404  // SetMachineBlockDevices sets the block devices visible on the machine.
  1405  func (m *Machine) SetMachineBlockDevices(info ...BlockDeviceInfo) error {
  1406  	return setMachineBlockDevices(m.st, m.Id(), info)
  1407  }
  1408  
  1409  // BlockDevices gets the aggregated list of block devices associated with
  1410  // the machine, including unprovisioned ones.
  1411  func (m *Machine) BlockDevices() ([]BlockDevice, error) {
  1412  	devices, err := getMachineBlockDevices(m.st, m.Id())
  1413  	if err != nil {
  1414  		return nil, err
  1415  	}
  1416  	result := make([]BlockDevice, len(devices))
  1417  	for i, dev := range devices {
  1418  		result[i] = dev
  1419  	}
  1420  	return result, nil
  1421  }