github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/volume.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	jujutxn "github.com/juju/txn"
    13  	"gopkg.in/juju/names.v2"
    14  	"gopkg.in/mgo.v2"
    15  	"gopkg.in/mgo.v2/bson"
    16  	"gopkg.in/mgo.v2/txn"
    17  
    18  	"github.com/juju/juju/status"
    19  	"github.com/juju/juju/storage"
    20  )
    21  
    22  // Volume describes a volume (disk, logical volume, etc.) in the model.
    23  type Volume interface {
    24  	GlobalEntity
    25  	LifeBinder
    26  	status.StatusGetter
    27  	status.StatusSetter
    28  
    29  	// VolumeTag returns the tag for the volume.
    30  	VolumeTag() names.VolumeTag
    31  
    32  	// StorageInstance returns the tag of the storage instance that this
    33  	// volume is assigned to, if any. If the volume is not assigned to
    34  	// a storage instance, an error satisfying errors.IsNotAssigned will
    35  	// be returned.
    36  	//
    37  	// A volume can be assigned to at most one storage instance, and a
    38  	// storage instance can have at most one associated volume.
    39  	StorageInstance() (names.StorageTag, error)
    40  
    41  	// Info returns the volume's VolumeInfo, or a NotProvisioned
    42  	// error if the volume has not yet been provisioned.
    43  	Info() (VolumeInfo, error)
    44  
    45  	// Params returns the parameters for provisioning the volume,
    46  	// if it has not already been provisioned. Params returns true if the
    47  	// returned parameters are usable for provisioning, otherwise false.
    48  	Params() (VolumeParams, bool)
    49  }
    50  
    51  // VolumeAttachment describes an attachment of a volume to a machine.
    52  type VolumeAttachment interface {
    53  	Lifer
    54  
    55  	// Volume returns the tag of the related Volume.
    56  	Volume() names.VolumeTag
    57  
    58  	// Machine returns the tag of the related Machine.
    59  	Machine() names.MachineTag
    60  
    61  	// Info returns the volume attachment's VolumeAttachmentInfo, or a
    62  	// NotProvisioned error if the attachment has not yet been made.
    63  	//
    64  	// TODO(axw) use a different error, rather than NotProvisioned
    65  	// (say, NotAttached or NotAssociated).
    66  	Info() (VolumeAttachmentInfo, error)
    67  
    68  	// Params returns the parameters for creating the volume attachment,
    69  	// if it has not already been made. Params returns true if the returned
    70  	// parameters are usable for creating an attachment, otherwise false.
    71  	Params() (VolumeAttachmentParams, bool)
    72  }
    73  
    74  type volume struct {
    75  	st  *State
    76  	doc volumeDoc
    77  }
    78  
    79  type volumeAttachment struct {
    80  	doc volumeAttachmentDoc
    81  }
    82  
    83  // volumeDoc records information about a volume in the model.
    84  type volumeDoc struct {
    85  	DocID           string        `bson:"_id"`
    86  	Name            string        `bson:"name"`
    87  	ModelUUID       string        `bson:"model-uuid"`
    88  	Life            Life          `bson:"life"`
    89  	StorageId       string        `bson:"storageid,omitempty"`
    90  	AttachmentCount int           `bson:"attachmentcount"`
    91  	Binding         string        `bson:"binding,omitempty"`
    92  	Info            *VolumeInfo   `bson:"info,omitempty"`
    93  	Params          *VolumeParams `bson:"params,omitempty"`
    94  }
    95  
    96  // volumeAttachmentDoc records information about a volume attachment.
    97  type volumeAttachmentDoc struct {
    98  	// DocID is the machine global key followed by the volume name.
    99  	DocID     string                  `bson:"_id"`
   100  	ModelUUID string                  `bson:"model-uuid"`
   101  	Volume    string                  `bson:"volumeid"`
   102  	Machine   string                  `bson:"machineid"`
   103  	Life      Life                    `bson:"life"`
   104  	Info      *VolumeAttachmentInfo   `bson:"info,omitempty"`
   105  	Params    *VolumeAttachmentParams `bson:"params,omitempty"`
   106  }
   107  
   108  // VolumeParams records parameters for provisioning a new volume.
   109  type VolumeParams struct {
   110  	// storage, if non-zero, is the tag of the storage instance
   111  	// that the volume is to be assigned to.
   112  	storage names.StorageTag
   113  
   114  	// binding, if non-nil, is the tag of the entity to which
   115  	// the volume's lifecycle will be bound.
   116  	binding names.Tag
   117  
   118  	Pool string `bson:"pool"`
   119  	Size uint64 `bson:"size"`
   120  }
   121  
   122  // VolumeInfo describes information about a volume.
   123  type VolumeInfo struct {
   124  	HardwareId string `bson:"hardwareid,omitempty"`
   125  	Size       uint64 `bson:"size"`
   126  	Pool       string `bson:"pool"`
   127  	VolumeId   string `bson:"volumeid"`
   128  	Persistent bool   `bson:"persistent"`
   129  }
   130  
   131  // VolumeAttachmentInfo describes information about a volume attachment.
   132  type VolumeAttachmentInfo struct {
   133  	DeviceName string `bson:"devicename,omitempty"`
   134  	DeviceLink string `bson:"devicelink,omitempty"`
   135  	BusAddress string `bson:"busaddress,omitempty"`
   136  	ReadOnly   bool   `bson:"read-only"`
   137  }
   138  
   139  // VolumeAttachmentParams records parameters for attaching a volume to a
   140  // machine.
   141  type VolumeAttachmentParams struct {
   142  	ReadOnly bool `bson:"read-only"`
   143  }
   144  
   145  // validate validates the contents of the volume document.
   146  func (v *volume) validate() error {
   147  	if v.doc.Binding != "" {
   148  		tag, err := names.ParseTag(v.doc.Binding)
   149  		if err != nil {
   150  			return errors.Annotate(err, "parsing binding")
   151  		}
   152  		switch tag.(type) {
   153  		case names.ModelTag:
   154  			// TODO(axw) support binding to model
   155  			return errors.NotSupportedf("binding to model")
   156  		case names.MachineTag:
   157  		case names.FilesystemTag:
   158  		case names.StorageTag:
   159  		default:
   160  			return errors.Errorf("invalid binding: %v", v.doc.Binding)
   161  		}
   162  	}
   163  	return nil
   164  }
   165  
   166  // globalKey is required to implement GlobalEntity.
   167  func (v *volume) globalKey() string {
   168  	return volumeGlobalKey(v.doc.Name)
   169  }
   170  
   171  // Tag is required to implement GlobalEntity.
   172  func (v *volume) Tag() names.Tag {
   173  	return v.VolumeTag()
   174  }
   175  
   176  // VolumeTag is required to implement Volume.
   177  func (v *volume) VolumeTag() names.VolumeTag {
   178  	return names.NewVolumeTag(v.doc.Name)
   179  }
   180  
   181  // Life returns the volume's current lifecycle state.
   182  func (v *volume) Life() Life {
   183  	return v.doc.Life
   184  }
   185  
   186  // LifeBinding is required to implement LifeBinder.
   187  //
   188  // Below is the set of possible entity types that a volume may be bound
   189  // to, and a description of the effects of doing so:
   190  //
   191  //   Machine:     If the volume is bound to a machine, then the volume
   192  //                will be destroyed when it is detached from the
   193  //                machine. It is not permitted for a volume to be
   194  //                attached to multiple machines while it is bound to a
   195  //                machine.
   196  //   Storage:     If the volume is bound to a storage instance, then
   197  //                the volume will be destroyed when the storage insance
   198  //                is removed from state.
   199  //   Filesystem:  If the volume is bound to a filesystem, i.e. the
   200  //                volume backs that filesystem, then it will be
   201  //                destroyed when the filesystem is removed from state.
   202  //   Model: If the volume is bound to the model, then the
   203  //                volume must be destroyed prior to the model
   204  //                being destroyed.
   205  func (v *volume) LifeBinding() names.Tag {
   206  	if v.doc.Binding == "" {
   207  		return nil
   208  	}
   209  	// Tag is validated in volume.validate.
   210  	tag, _ := names.ParseTag(v.doc.Binding)
   211  	return tag
   212  }
   213  
   214  // StorageInstance is required to implement Volume.
   215  func (v *volume) StorageInstance() (names.StorageTag, error) {
   216  	if v.doc.StorageId == "" {
   217  		msg := fmt.Sprintf("volume %q is not assigned to any storage instance", v.Tag().Id())
   218  		return names.StorageTag{}, errors.NewNotAssigned(nil, msg)
   219  	}
   220  	return names.NewStorageTag(v.doc.StorageId), nil
   221  }
   222  
   223  // Info is required to implement Volume.
   224  func (v *volume) Info() (VolumeInfo, error) {
   225  	if v.doc.Info == nil {
   226  		return VolumeInfo{}, errors.NotProvisionedf("volume %q", v.doc.Name)
   227  	}
   228  	return *v.doc.Info, nil
   229  }
   230  
   231  // Params is required to implement Volume.
   232  func (v *volume) Params() (VolumeParams, bool) {
   233  	if v.doc.Params == nil {
   234  		return VolumeParams{}, false
   235  	}
   236  	return *v.doc.Params, true
   237  }
   238  
   239  // Status is required to implement StatusGetter.
   240  func (v *volume) Status() (status.StatusInfo, error) {
   241  	return v.st.VolumeStatus(v.VolumeTag())
   242  }
   243  
   244  // SetStatus is required to implement StatusSetter.
   245  func (v *volume) SetStatus(volumeStatus status.StatusInfo) error {
   246  	return v.st.SetVolumeStatus(v.VolumeTag(), volumeStatus.Status, volumeStatus.Message, volumeStatus.Data, volumeStatus.Since)
   247  }
   248  
   249  // Volume is required to implement VolumeAttachment.
   250  func (v *volumeAttachment) Volume() names.VolumeTag {
   251  	return names.NewVolumeTag(v.doc.Volume)
   252  }
   253  
   254  // Machine is required to implement VolumeAttachment.
   255  func (v *volumeAttachment) Machine() names.MachineTag {
   256  	return names.NewMachineTag(v.doc.Machine)
   257  }
   258  
   259  // Life is required to implement VolumeAttachment.
   260  func (v *volumeAttachment) Life() Life {
   261  	return v.doc.Life
   262  }
   263  
   264  // Info is required to implement VolumeAttachment.
   265  func (v *volumeAttachment) Info() (VolumeAttachmentInfo, error) {
   266  	if v.doc.Info == nil {
   267  		return VolumeAttachmentInfo{}, errors.NotProvisionedf("volume attachment %q on %q", v.doc.Volume, v.doc.Machine)
   268  	}
   269  	return *v.doc.Info, nil
   270  }
   271  
   272  // Params is required to implement VolumeAttachment.
   273  func (v *volumeAttachment) Params() (VolumeAttachmentParams, bool) {
   274  	if v.doc.Params == nil {
   275  		return VolumeAttachmentParams{}, false
   276  	}
   277  	return *v.doc.Params, true
   278  }
   279  
   280  // Volume returns the Volume with the specified name.
   281  func (st *State) Volume(tag names.VolumeTag) (Volume, error) {
   282  	v, err := st.volumeByTag(tag)
   283  	return v, err
   284  }
   285  
   286  func (st *State) volumes(query interface{}) ([]*volume, error) {
   287  	coll, cleanup := st.getCollection(volumesC)
   288  	defer cleanup()
   289  
   290  	var docs []volumeDoc
   291  	err := coll.Find(query).All(&docs)
   292  	if err != nil {
   293  		return nil, errors.Annotate(err, "querying volumes")
   294  	}
   295  	volumes := make([]*volume, len(docs))
   296  	for i := range docs {
   297  		volume := &volume{st, docs[i]}
   298  		if err := volume.validate(); err != nil {
   299  			return nil, errors.Annotate(err, "validating volume")
   300  		}
   301  		volumes[i] = volume
   302  	}
   303  	return volumes, nil
   304  }
   305  
   306  func (st *State) volume(query bson.D, description string) (*volume, error) {
   307  	volumes, err := st.volumes(query)
   308  	if err != nil {
   309  		return nil, errors.Trace(err)
   310  	}
   311  	if len(volumes) == 0 {
   312  		return nil, errors.NotFoundf("%s", description)
   313  	} else if len(volumes) != 1 {
   314  		return nil, errors.Errorf("expected 1 volume, got %d", len(volumes))
   315  	}
   316  	return volumes[0], nil
   317  }
   318  
   319  func (st *State) volumeByTag(tag names.VolumeTag) (*volume, error) {
   320  	return st.volume(bson.D{{"_id", tag.Id()}}, fmt.Sprintf("volume %q", tag.Id()))
   321  }
   322  
   323  func volumesToInterfaces(volumes []*volume) []Volume {
   324  	result := make([]Volume, len(volumes))
   325  	for i, v := range volumes {
   326  		result[i] = v
   327  	}
   328  	return result
   329  }
   330  
   331  func (st *State) storageInstanceVolume(tag names.StorageTag) (*volume, error) {
   332  	return st.volume(
   333  		bson.D{{"storageid", tag.Id()}},
   334  		fmt.Sprintf("volume for storage instance %q", tag.Id()),
   335  	)
   336  }
   337  
   338  // StorageInstanceVolume returns the Volume assigned to the specified
   339  // storage instance.
   340  func (st *State) StorageInstanceVolume(tag names.StorageTag) (Volume, error) {
   341  	v, err := st.storageInstanceVolume(tag)
   342  	return v, err
   343  }
   344  
   345  // VolumeAttachment returns the VolumeAttachment corresponding to
   346  // the specified volume and machine.
   347  func (st *State) VolumeAttachment(machine names.MachineTag, volume names.VolumeTag) (VolumeAttachment, error) {
   348  	coll, cleanup := st.getCollection(volumeAttachmentsC)
   349  	defer cleanup()
   350  
   351  	var att volumeAttachment
   352  	err := coll.FindId(volumeAttachmentId(machine.Id(), volume.Id())).One(&att.doc)
   353  	if err == mgo.ErrNotFound {
   354  		return nil, errors.NotFoundf("volume %q on machine %q", volume.Id(), machine.Id())
   355  	} else if err != nil {
   356  		return nil, errors.Annotatef(err, "getting volume %q on machine %q", volume.Id(), machine.Id())
   357  	}
   358  	return &att, nil
   359  }
   360  
   361  // MachineVolumeAttachments returns all of the VolumeAttachments for the
   362  // specified machine.
   363  func (st *State) MachineVolumeAttachments(machine names.MachineTag) ([]VolumeAttachment, error) {
   364  	attachments, err := st.volumeAttachments(bson.D{{"machineid", machine.Id()}})
   365  	if err != nil {
   366  		return nil, errors.Annotatef(err, "getting volume attachments for machine %q", machine.Id())
   367  	}
   368  	return attachments, nil
   369  }
   370  
   371  // VolumeAttachments returns all of the VolumeAttachments for the specified
   372  // volume.
   373  func (st *State) VolumeAttachments(volume names.VolumeTag) ([]VolumeAttachment, error) {
   374  	attachments, err := st.volumeAttachments(bson.D{{"volumeid", volume.Id()}})
   375  	if err != nil {
   376  		return nil, errors.Annotatef(err, "getting volume attachments for volume %q", volume.Id())
   377  	}
   378  	return attachments, nil
   379  }
   380  
   381  func (st *State) volumeAttachments(query bson.D) ([]VolumeAttachment, error) {
   382  	coll, cleanup := st.getCollection(volumeAttachmentsC)
   383  	defer cleanup()
   384  
   385  	var docs []volumeAttachmentDoc
   386  	err := coll.Find(query).All(&docs)
   387  	if err == mgo.ErrNotFound {
   388  		return nil, nil
   389  	} else if err != nil {
   390  		return nil, errors.Trace(err)
   391  	}
   392  	attachments := make([]VolumeAttachment, len(docs))
   393  	for i, doc := range docs {
   394  		attachments[i] = &volumeAttachment{doc}
   395  	}
   396  	return attachments, nil
   397  }
   398  
   399  type errContainsFilesystem struct {
   400  	error
   401  }
   402  
   403  func IsContainsFilesystem(err error) bool {
   404  	_, ok := errors.Cause(err).(*errContainsFilesystem)
   405  	return ok
   406  }
   407  
   408  // removeMachineVolumesOps returns txn.Ops to remove non-persistent volumes
   409  // attached to the specified machine. This is used when the given machine is
   410  // being removed from state.
   411  func (st *State) removeMachineVolumesOps(machine names.MachineTag) ([]txn.Op, error) {
   412  	attachments, err := st.MachineVolumeAttachments(machine)
   413  	if err != nil {
   414  		return nil, errors.Trace(err)
   415  	}
   416  	ops := make([]txn.Op, 0, len(attachments))
   417  	for _, a := range attachments {
   418  		volumeTag := a.Volume()
   419  		// When removing the machine, there should only remain
   420  		// non-persistent storage. This will be implicitly
   421  		// removed when the machine is removed, so we do not
   422  		// use removeVolumeAttachmentOps or removeVolumeOps,
   423  		// which track and update related documents.
   424  		ops = append(ops, txn.Op{
   425  			C:      volumeAttachmentsC,
   426  			Id:     volumeAttachmentId(machine.Id(), volumeTag.Id()),
   427  			Assert: txn.DocExists,
   428  			Remove: true,
   429  		})
   430  		canRemove, err := isVolumeInherentlyMachineBound(st, volumeTag)
   431  		if err != nil {
   432  			return nil, errors.Trace(err)
   433  		}
   434  		if !canRemove {
   435  			return nil, errors.Errorf("machine has non-machine bound volume %v", volumeTag.Id())
   436  		}
   437  		ops = append(ops, txn.Op{
   438  			C:      volumesC,
   439  			Id:     volumeTag.Id(),
   440  			Assert: txn.DocExists,
   441  			Remove: true,
   442  		})
   443  	}
   444  	return ops, nil
   445  }
   446  
   447  // isVolumeInherentlyMachineBound reports whether or not the volume with the
   448  // specified tag is inherently bound to the lifetime of the machine, and will
   449  // be removed along with it, leaving no resources dangling.
   450  func isVolumeInherentlyMachineBound(st *State, tag names.VolumeTag) (bool, error) {
   451  	volume, err := st.Volume(tag)
   452  	if err != nil {
   453  		return false, errors.Trace(err)
   454  	}
   455  	volumeInfo, err := volume.Info()
   456  	if errors.IsNotProvisioned(err) {
   457  		params, _ := volume.Params()
   458  		_, provider, err := poolStorageProvider(st, params.Pool)
   459  		if err != nil {
   460  			return false, errors.Trace(err)
   461  		}
   462  		if provider.Scope() == storage.ScopeMachine {
   463  			// Any storage created by a machine must be destroyed
   464  			// along with the machine.
   465  			return true, nil
   466  		}
   467  		if provider.Dynamic() {
   468  			// We don't know ahead of time whether the storage
   469  			// will be Persistent, so we assume it will be, and
   470  			// rely on the environment-level storage provisioner
   471  			// to clean up.
   472  			return false, nil
   473  		}
   474  		// Volume is static, so even if it is provisioned, it will
   475  		// be tied to the machine.
   476  		return true, nil
   477  	} else if err != nil {
   478  		return false, errors.Trace(err)
   479  	}
   480  	// If volume does not outlive machine it can be removed.
   481  	return !volumeInfo.Persistent, nil
   482  }
   483  
   484  // DetachVolume marks the volume attachment identified by the specified machine
   485  // and volume tags as Dying, if it is Alive. DetachVolume will fail with a
   486  // IsContainsFilesystem error if the volume contains an attached filesystem; the
   487  // filesystem attachment must be removed first.
   488  func (st *State) DetachVolume(machine names.MachineTag, volume names.VolumeTag) (err error) {
   489  	defer errors.DeferredAnnotatef(&err, "detaching volume %s from machine %s", volume.Id(), machine.Id())
   490  	// If the volume is backing a filesystem, the volume cannot be detached
   491  	// until the filesystem has been detached.
   492  	if _, err := st.volumeFilesystemAttachment(machine, volume); err == nil {
   493  		return &errContainsFilesystem{errors.New("volume contains attached filesystem")}
   494  	} else if !errors.IsNotFound(err) {
   495  		return errors.Trace(err)
   496  	}
   497  	buildTxn := func(attempt int) ([]txn.Op, error) {
   498  		va, err := st.VolumeAttachment(machine, volume)
   499  		if err != nil {
   500  			return nil, errors.Trace(err)
   501  		}
   502  		if va.Life() != Alive {
   503  			return nil, jujutxn.ErrNoOperations
   504  		}
   505  		return detachVolumeOps(machine, volume), nil
   506  	}
   507  	return st.run(buildTxn)
   508  }
   509  
   510  func (st *State) volumeFilesystemAttachment(machine names.MachineTag, volume names.VolumeTag) (FilesystemAttachment, error) {
   511  	filesystem, err := st.VolumeFilesystem(volume)
   512  	if err != nil {
   513  		return nil, errors.Trace(err)
   514  	}
   515  	return st.FilesystemAttachment(machine, filesystem.FilesystemTag())
   516  }
   517  
   518  func detachVolumeOps(m names.MachineTag, v names.VolumeTag) []txn.Op {
   519  	return []txn.Op{{
   520  		C:      volumeAttachmentsC,
   521  		Id:     volumeAttachmentId(m.Id(), v.Id()),
   522  		Assert: isAliveDoc,
   523  		Update: bson.D{{"$set", bson.D{{"life", Dying}}}},
   524  	}}
   525  }
   526  
   527  // RemoveVolumeAttachment removes the volume attachment from state.
   528  // RemoveVolumeAttachment will fail if the attachment is not Dying.
   529  func (st *State) RemoveVolumeAttachment(machine names.MachineTag, volume names.VolumeTag) (err error) {
   530  	defer errors.DeferredAnnotatef(&err, "removing attachment of volume %s from machine %s", volume.Id(), machine.Id())
   531  	buildTxn := func(attempt int) ([]txn.Op, error) {
   532  		attachment, err := st.VolumeAttachment(machine, volume)
   533  		if errors.IsNotFound(err) && attempt > 0 {
   534  			// We only ignore IsNotFound on attempts after the
   535  			// first, since we expect the volume attachment to
   536  			// be there initially.
   537  			return nil, jujutxn.ErrNoOperations
   538  		}
   539  		if err != nil {
   540  			return nil, errors.Trace(err)
   541  		}
   542  		if attachment.Life() != Dying {
   543  			return nil, errors.New("volume attachment is not dying")
   544  		}
   545  		v, err := st.volumeByTag(volume)
   546  		if err != nil {
   547  			return nil, errors.Trace(err)
   548  		}
   549  		return removeVolumeAttachmentOps(machine, v), nil
   550  	}
   551  	return st.run(buildTxn)
   552  }
   553  
   554  func removeVolumeAttachmentOps(m names.MachineTag, v *volume) []txn.Op {
   555  	decrefVolumeOp := machineStorageDecrefOp(
   556  		volumesC, v.doc.Name,
   557  		v.doc.AttachmentCount, v.doc.Life,
   558  		m, v.doc.Binding,
   559  	)
   560  	return []txn.Op{{
   561  		C:      volumeAttachmentsC,
   562  		Id:     volumeAttachmentId(m.Id(), v.doc.Name),
   563  		Assert: bson.D{{"life", Dying}},
   564  		Remove: true,
   565  	}, decrefVolumeOp, {
   566  		C:      machinesC,
   567  		Id:     m.Id(),
   568  		Assert: txn.DocExists,
   569  		Update: bson.D{{"$pull", bson.D{{"volumes", v.doc.Name}}}},
   570  	}}
   571  }
   572  
   573  // machineStorageDecrefOp returns a txn.Op that will decrement the attachment
   574  // count for a given machine storage entity (volume or filesystem), given its
   575  // current attachment count and lifecycle state. If the attachment count goes
   576  // to zero, then the entity should become Dead.
   577  func machineStorageDecrefOp(
   578  	collection, id string,
   579  	attachmentCount int, life Life,
   580  	machine names.MachineTag,
   581  	binding string,
   582  ) txn.Op {
   583  	op := txn.Op{
   584  		C:  collection,
   585  		Id: id,
   586  	}
   587  	if life == Dying {
   588  		if attachmentCount == 1 {
   589  			// This is the last attachment: the volume can be
   590  			// marked Dead. There can be no concurrent attachments
   591  			// since it is Dying.
   592  			op.Assert = bson.D{
   593  				{"life", Dying},
   594  				{"attachmentcount", 1},
   595  			}
   596  			op.Update = bson.D{
   597  				{"$inc", bson.D{{"attachmentcount", -1}}},
   598  				{"$set", bson.D{{"life", Dead}}},
   599  			}
   600  		} else {
   601  			// This is not the last attachment; just decref,
   602  			// allowing for concurrent attachment removals but
   603  			// ensuring we don't drop to zero without marking
   604  			// the volume Dead.
   605  			op.Assert = bson.D{
   606  				{"life", Dying},
   607  				{"attachmentcount", bson.D{{"$gt", 1}}},
   608  			}
   609  			op.Update = bson.D{
   610  				{"$inc", bson.D{{"attachmentcount", -1}}},
   611  			}
   612  		}
   613  	} else {
   614  		// The volume is still Alive: decref, retrying if the
   615  		// volume is destroyed concurrently or the binding changes.
   616  		// If the volume is bound to the machine, advance it to
   617  		// Dead; binding storage to a machine and attaching the
   618  		// storage to multiple machines will be mutually exclusive.
   619  		//
   620  		// Otherwise, when DestroyVolume is called, the volume will
   621  		// be marked Dead if it has no attachments.
   622  		update := bson.D{
   623  			{"$inc", bson.D{{"attachmentcount", -1}}},
   624  		}
   625  		if binding == machine.String() {
   626  			update = append(update, bson.DocElem{
   627  				"$set", bson.D{{"life", Dead}},
   628  			})
   629  		}
   630  		op.Assert = bson.D{
   631  			{"life", Alive},
   632  			{"binding", binding},
   633  			{"attachmentcount", bson.D{{"$gt", 0}}},
   634  		}
   635  		op.Update = update
   636  	}
   637  	return op
   638  }
   639  
   640  // DestroyVolume ensures that the volume and any attachments to it will be
   641  // destroyed and removed from state at some point in the future. DestroyVolume
   642  // will fail with an IsContainsFilesystem error if the volume contains a
   643  // filesystem; the filesystem must be fully removed first.
   644  func (st *State) DestroyVolume(tag names.VolumeTag) (err error) {
   645  	defer errors.DeferredAnnotatef(&err, "destroying volume %s", tag.Id())
   646  	if _, err := st.VolumeFilesystem(tag); err == nil {
   647  		return &errContainsFilesystem{errors.New("volume contains filesystem")}
   648  	} else if !errors.IsNotFound(err) {
   649  		return errors.Trace(err)
   650  	}
   651  	buildTxn := func(attempt int) ([]txn.Op, error) {
   652  		volume, err := st.volumeByTag(tag)
   653  		if errors.IsNotFound(err) {
   654  			return nil, jujutxn.ErrNoOperations
   655  		} else if err != nil {
   656  			return nil, errors.Trace(err)
   657  		}
   658  		if volume.Life() != Alive {
   659  			return nil, jujutxn.ErrNoOperations
   660  		}
   661  		return destroyVolumeOps(st, volume), nil
   662  	}
   663  	return st.run(buildTxn)
   664  }
   665  
   666  func destroyVolumeOps(st *State, v *volume) []txn.Op {
   667  	if v.doc.AttachmentCount == 0 {
   668  		hasNoAttachments := bson.D{{"attachmentcount", 0}}
   669  		return []txn.Op{{
   670  			C:      volumesC,
   671  			Id:     v.doc.Name,
   672  			Assert: append(hasNoAttachments, isAliveDoc...),
   673  			Update: bson.D{{"$set", bson.D{{"life", Dead}}}},
   674  		}}
   675  	}
   676  	cleanupOp := newCleanupOp(cleanupAttachmentsForDyingVolume, v.doc.Name)
   677  	hasAttachments := bson.D{{"attachmentcount", bson.D{{"$gt", 0}}}}
   678  	return []txn.Op{{
   679  		C:      volumesC,
   680  		Id:     v.doc.Name,
   681  		Assert: append(hasAttachments, isAliveDoc...),
   682  		Update: bson.D{{"$set", bson.D{{"life", Dying}}}},
   683  	}, cleanupOp}
   684  }
   685  
   686  // RemoveVolume removes the volume from state. RemoveVolume will fail if
   687  // the volume is not Dead, which implies that it still has attachments.
   688  func (st *State) RemoveVolume(tag names.VolumeTag) (err error) {
   689  	defer errors.DeferredAnnotatef(&err, "removing volume %s", tag.Id())
   690  	buildTxn := func(attempt int) ([]txn.Op, error) {
   691  		volume, err := st.Volume(tag)
   692  		if errors.IsNotFound(err) {
   693  			return nil, jujutxn.ErrNoOperations
   694  		} else if err != nil {
   695  			return nil, errors.Trace(err)
   696  		}
   697  		if volume.Life() != Dead {
   698  			return nil, errors.New("volume is not dead")
   699  		}
   700  		return []txn.Op{
   701  			{
   702  				C:      volumesC,
   703  				Id:     tag.Id(),
   704  				Assert: txn.DocExists,
   705  				Remove: true,
   706  			},
   707  			removeStatusOp(st, volumeGlobalKey(tag.Id())),
   708  		}, nil
   709  	}
   710  	return st.run(buildTxn)
   711  }
   712  
   713  // newVolumeName returns a unique volume name.
   714  // If the machine ID supplied is non-empty, the
   715  // volume ID will incorporate it as the volume's
   716  // machine scope.
   717  func newVolumeName(st *State, machineId string) (string, error) {
   718  	seq, err := st.sequence("volume")
   719  	if err != nil {
   720  		return "", errors.Trace(err)
   721  	}
   722  	id := fmt.Sprint(seq)
   723  	if machineId != "" {
   724  		id = machineId + "/" + id
   725  	}
   726  	return id, nil
   727  }
   728  
   729  // addVolumeOps returns txn.Ops to create a new volume with the specified
   730  // parameters. If the supplied machine ID is non-empty, and the storage
   731  // provider is machine-scoped, then the volume will be scoped to that
   732  // machine.
   733  func (st *State) addVolumeOps(params VolumeParams, machineId string) ([]txn.Op, names.VolumeTag, error) {
   734  	if params.binding == nil {
   735  		params.binding = names.NewMachineTag(machineId)
   736  	}
   737  	params, err := st.volumeParamsWithDefaults(params)
   738  	if err != nil {
   739  		return nil, names.VolumeTag{}, errors.Trace(err)
   740  	}
   741  	machineId, err = st.validateVolumeParams(params, machineId)
   742  	if err != nil {
   743  		return nil, names.VolumeTag{}, errors.Annotate(err, "validating volume params")
   744  	}
   745  	name, err := newVolumeName(st, machineId)
   746  	if err != nil {
   747  		return nil, names.VolumeTag{}, errors.Annotate(err, "cannot generate volume name")
   748  	}
   749  	status := statusDoc{
   750  		Status:  status.Pending,
   751  		Updated: st.clock.Now().UnixNano(),
   752  	}
   753  	doc := volumeDoc{
   754  		Name:      name,
   755  		StorageId: params.storage.Id(),
   756  		Binding:   params.binding.String(),
   757  		Params:    &params,
   758  		// Every volume is created with one attachment.
   759  		AttachmentCount: 1,
   760  	}
   761  	return st.newVolumeOps(doc, status), names.NewVolumeTag(name), nil
   762  }
   763  
   764  func (st *State) newVolumeOps(doc volumeDoc, status statusDoc) []txn.Op {
   765  	return []txn.Op{
   766  		createStatusOp(st, volumeGlobalKey(doc.Name), status),
   767  		{
   768  			C:      volumesC,
   769  			Id:     doc.Name,
   770  			Assert: txn.DocMissing,
   771  			Insert: &doc,
   772  		},
   773  	}
   774  }
   775  
   776  func (st *State) volumeParamsWithDefaults(params VolumeParams) (VolumeParams, error) {
   777  	if params.Pool != "" {
   778  		return params, nil
   779  	}
   780  	envConfig, err := st.ModelConfig()
   781  	if err != nil {
   782  		return VolumeParams{}, errors.Trace(err)
   783  	}
   784  	cons := StorageConstraints{
   785  		Pool:  params.Pool,
   786  		Size:  params.Size,
   787  		Count: 1,
   788  	}
   789  	poolName, err := defaultStoragePool(envConfig, storage.StorageKindBlock, cons)
   790  	if err != nil {
   791  		return VolumeParams{}, errors.Annotate(err, "getting default block storage pool")
   792  	}
   793  	params.Pool = poolName
   794  	return params, nil
   795  }
   796  
   797  // validateVolumeParams validates the volume parameters, and returns the
   798  // machine ID to use as the scope in the volume tag.
   799  func (st *State) validateVolumeParams(params VolumeParams, machineId string) (maybeMachineId string, _ error) {
   800  	if err := validateStoragePool(st, params.Pool, storage.StorageKindBlock, &machineId); err != nil {
   801  		return "", err
   802  	}
   803  	if params.Size == 0 {
   804  		return "", errors.New("invalid size 0")
   805  	}
   806  	return machineId, nil
   807  }
   808  
   809  // volumeAttachmentId returns a volume attachment document ID,
   810  // given the corresponding volume name and machine ID.
   811  func volumeAttachmentId(machineId, volumeName string) string {
   812  	return fmt.Sprintf("%s:%s", machineId, volumeName)
   813  }
   814  
   815  // ParseVolumeAttachmentId parses a string as a volume attachment ID,
   816  // returning the machine and volume components.
   817  func ParseVolumeAttachmentId(id string) (names.MachineTag, names.VolumeTag, error) {
   818  	fields := strings.SplitN(id, ":", 2)
   819  	if len(fields) != 2 || !names.IsValidMachine(fields[0]) || !names.IsValidVolume(fields[1]) {
   820  		return names.MachineTag{}, names.VolumeTag{}, errors.Errorf("invalid volume attachment ID %q", id)
   821  	}
   822  	machineTag := names.NewMachineTag(fields[0])
   823  	volumeTag := names.NewVolumeTag(fields[1])
   824  	return machineTag, volumeTag, nil
   825  }
   826  
   827  type volumeAttachmentTemplate struct {
   828  	tag    names.VolumeTag
   829  	params VolumeAttachmentParams
   830  }
   831  
   832  // createMachineVolumeAttachmentInfo creates volume attachments
   833  // for the specified machine, and attachment parameters keyed
   834  // by volume tags. The caller is responsible for incrementing
   835  // the volume's attachmentcount field.
   836  func createMachineVolumeAttachmentsOps(machineId string, attachments []volumeAttachmentTemplate) []txn.Op {
   837  	ops := make([]txn.Op, len(attachments))
   838  	for i, attachment := range attachments {
   839  		paramsCopy := attachment.params
   840  		ops[i] = txn.Op{
   841  			C:      volumeAttachmentsC,
   842  			Id:     volumeAttachmentId(machineId, attachment.tag.Id()),
   843  			Assert: txn.DocMissing,
   844  			Insert: &volumeAttachmentDoc{
   845  				Volume:  attachment.tag.Id(),
   846  				Machine: machineId,
   847  				Params:  &paramsCopy,
   848  			},
   849  		}
   850  	}
   851  	return ops
   852  }
   853  
   854  // setMachineVolumeAttachmentInfo sets the volume attachment
   855  // info for the specified machine. Each volume attachment info
   856  // structure is keyed by the name of the volume it corresponds
   857  // to.
   858  func setMachineVolumeAttachmentInfo(
   859  	st *State,
   860  	machineId string,
   861  	attachments map[names.VolumeTag]VolumeAttachmentInfo,
   862  ) (err error) {
   863  	defer errors.DeferredAnnotatef(&err, "cannot set volume attachment info for machine %s", machineId)
   864  	machineTag := names.NewMachineTag(machineId)
   865  	for volumeTag, info := range attachments {
   866  		if err := st.setVolumeAttachmentInfo(machineTag, volumeTag, info); err != nil {
   867  			return errors.Annotatef(err, "setting attachment info for volume %s", volumeTag.Id())
   868  		}
   869  	}
   870  	return nil
   871  }
   872  
   873  // SetVolumeAttachmentInfo sets the VolumeAttachmentInfo for the specified
   874  // volume attachment.
   875  func (st *State) SetVolumeAttachmentInfo(machineTag names.MachineTag, volumeTag names.VolumeTag, info VolumeAttachmentInfo) (err error) {
   876  	defer errors.DeferredAnnotatef(&err, "cannot set info for volume attachment %s:%s", volumeTag.Id(), machineTag.Id())
   877  	v, err := st.Volume(volumeTag)
   878  	if err != nil {
   879  		return errors.Trace(err)
   880  	}
   881  	// Ensure volume is provisioned before setting attachment info.
   882  	// A volume cannot go from being provisioned to unprovisioned,
   883  	// so there is no txn.Op for this below.
   884  	if _, err := v.Info(); err != nil {
   885  		return errors.Trace(err)
   886  	}
   887  	// Also ensure the machine is provisioned.
   888  	m, err := st.Machine(machineTag.Id())
   889  	if err != nil {
   890  		return errors.Trace(err)
   891  	}
   892  	if _, err := m.InstanceId(); err != nil {
   893  		return errors.Trace(err)
   894  	}
   895  	return st.setVolumeAttachmentInfo(machineTag, volumeTag, info)
   896  }
   897  
   898  func (st *State) setVolumeAttachmentInfo(
   899  	machineTag names.MachineTag,
   900  	volumeTag names.VolumeTag,
   901  	info VolumeAttachmentInfo,
   902  ) error {
   903  	buildTxn := func(attempt int) ([]txn.Op, error) {
   904  		va, err := st.VolumeAttachment(machineTag, volumeTag)
   905  		if err != nil {
   906  			return nil, errors.Trace(err)
   907  		}
   908  		// If the volume attachment has parameters, unset them
   909  		// when we set info for the first time, ensuring that
   910  		// params and info are mutually exclusive.
   911  		_, unsetParams := va.Params()
   912  		ops := setVolumeAttachmentInfoOps(
   913  			machineTag, volumeTag, info, unsetParams,
   914  		)
   915  		return ops, nil
   916  	}
   917  	return st.run(buildTxn)
   918  }
   919  
   920  func setVolumeAttachmentInfoOps(machine names.MachineTag, volume names.VolumeTag, info VolumeAttachmentInfo, unsetParams bool) []txn.Op {
   921  	asserts := isAliveDoc
   922  	update := bson.D{
   923  		{"$set", bson.D{{"info", &info}}},
   924  	}
   925  	if unsetParams {
   926  		asserts = append(asserts, bson.DocElem{"info", bson.D{{"$exists", false}}})
   927  		asserts = append(asserts, bson.DocElem{"params", bson.D{{"$exists", true}}})
   928  		update = append(update, bson.DocElem{"$unset", bson.D{{"params", nil}}})
   929  	}
   930  	return []txn.Op{{
   931  		C:      volumeAttachmentsC,
   932  		Id:     volumeAttachmentId(machine.Id(), volume.Id()),
   933  		Assert: asserts,
   934  		Update: update,
   935  	}}
   936  }
   937  
   938  // setProvisionedVolumeInfo sets the initial info for newly
   939  // provisioned volumes. If non-empty, machineId must be the
   940  // machine ID associated with the volumes.
   941  func setProvisionedVolumeInfo(st *State, volumes map[names.VolumeTag]VolumeInfo) error {
   942  	for volumeTag, info := range volumes {
   943  		if err := st.SetVolumeInfo(volumeTag, info); err != nil {
   944  			return errors.Trace(err)
   945  		}
   946  	}
   947  	return nil
   948  }
   949  
   950  // SetVolumeInfo sets the VolumeInfo for the specified volume.
   951  func (st *State) SetVolumeInfo(tag names.VolumeTag, info VolumeInfo) (err error) {
   952  	defer errors.DeferredAnnotatef(&err, "cannot set info for volume %q", tag.Id())
   953  	if info.VolumeId == "" {
   954  		return errors.New("volume ID not set")
   955  	}
   956  	// TODO(axw) we should reject info without VolumeId set; can't do this
   957  	// until the providers all set it correctly.
   958  	buildTxn := func(attempt int) ([]txn.Op, error) {
   959  		v, err := st.Volume(tag)
   960  		if err != nil {
   961  			return nil, errors.Trace(err)
   962  		}
   963  		// If the volume has parameters, unset them when
   964  		// we set info for the first time, ensuring that
   965  		// params and info are mutually exclusive.
   966  		var unsetParams bool
   967  		var ops []txn.Op
   968  		if params, ok := v.Params(); ok {
   969  			info.Pool = params.Pool
   970  			unsetParams = true
   971  		} else {
   972  			// Ensure immutable properties do not change.
   973  			oldInfo, err := v.Info()
   974  			if err != nil {
   975  				return nil, err
   976  			}
   977  			if err := validateVolumeInfoChange(info, oldInfo); err != nil {
   978  				return nil, err
   979  			}
   980  		}
   981  		ops = append(ops, setVolumeInfoOps(tag, info, unsetParams)...)
   982  		return ops, nil
   983  	}
   984  	return st.run(buildTxn)
   985  }
   986  
   987  func validateVolumeInfoChange(newInfo, oldInfo VolumeInfo) error {
   988  	if newInfo.Pool != oldInfo.Pool {
   989  		return errors.Errorf(
   990  			"cannot change pool from %q to %q",
   991  			oldInfo.Pool, newInfo.Pool,
   992  		)
   993  	}
   994  	if newInfo.VolumeId != oldInfo.VolumeId {
   995  		return errors.Errorf(
   996  			"cannot change volume ID from %q to %q",
   997  			oldInfo.VolumeId, newInfo.VolumeId,
   998  		)
   999  	}
  1000  	return nil
  1001  }
  1002  
  1003  func setVolumeInfoOps(tag names.VolumeTag, info VolumeInfo, unsetParams bool) []txn.Op {
  1004  	asserts := isAliveDoc
  1005  	update := bson.D{
  1006  		{"$set", bson.D{{"info", &info}}},
  1007  	}
  1008  	if unsetParams {
  1009  		asserts = append(asserts, bson.DocElem{"info", bson.D{{"$exists", false}}})
  1010  		asserts = append(asserts, bson.DocElem{"params", bson.D{{"$exists", true}}})
  1011  		update = append(update, bson.DocElem{"$unset", bson.D{{"params", nil}}})
  1012  	}
  1013  	return []txn.Op{{
  1014  		C:      volumesC,
  1015  		Id:     tag.Id(),
  1016  		Assert: asserts,
  1017  		Update: update,
  1018  	}}
  1019  }
  1020  
  1021  // AllVolumes returns all Volumes scoped to the model.
  1022  func (st *State) AllVolumes() ([]Volume, error) {
  1023  	volumes, err := st.volumes(nil)
  1024  	if err != nil {
  1025  		return nil, errors.Annotate(err, "cannot get volumes")
  1026  	}
  1027  	return volumesToInterfaces(volumes), nil
  1028  }
  1029  
  1030  func volumeGlobalKey(name string) string {
  1031  	return "v#" + name
  1032  }
  1033  
  1034  // VolumeStatus returns the status of the specified volume.
  1035  func (st *State) VolumeStatus(tag names.VolumeTag) (status.StatusInfo, error) {
  1036  	return getStatus(st, volumeGlobalKey(tag.Id()), "volume")
  1037  }
  1038  
  1039  // SetVolumeStatus sets the status of the specified volume.
  1040  func (st *State) SetVolumeStatus(tag names.VolumeTag, volumeStatus status.Status, info string, data map[string]interface{}, updated *time.Time) error {
  1041  	switch volumeStatus {
  1042  	case status.Attaching, status.Attached, status.Detaching, status.Detached, status.Destroying:
  1043  	case status.Error:
  1044  		if info == "" {
  1045  			return errors.Errorf("cannot set status %q without info", volumeStatus)
  1046  		}
  1047  	case status.Pending:
  1048  		// If a volume is not yet provisioned, we allow its status
  1049  		// to be set back to pending (when a retry is to occur).
  1050  		v, err := st.Volume(tag)
  1051  		if err != nil {
  1052  			return errors.Trace(err)
  1053  		}
  1054  		_, err = v.Info()
  1055  		if errors.IsNotProvisioned(err) {
  1056  			break
  1057  		}
  1058  		return errors.Errorf("cannot set status %q", volumeStatus)
  1059  	default:
  1060  		return errors.Errorf("cannot set invalid status %q", volumeStatus)
  1061  	}
  1062  	return setStatus(st, setStatusParams{
  1063  		badge:     "volume",
  1064  		globalKey: volumeGlobalKey(tag.Id()),
  1065  		status:    volumeStatus,
  1066  		message:   info,
  1067  		rawData:   data,
  1068  		updated:   updated,
  1069  	})
  1070  }