github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/storageprovisioner/filesystem_events.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storageprovisioner
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/apiserver/params"
    11  	"github.com/juju/juju/instance"
    12  	"github.com/juju/juju/storage"
    13  	"github.com/juju/juju/watcher"
    14  )
    15  
    16  // filesystemsChanged is called when the lifecycle states of the filesystems
    17  // with the provided IDs have been seen to have changed.
    18  func filesystemsChanged(ctx *context, changes []string) error {
    19  	tags := make([]names.Tag, len(changes))
    20  	for i, change := range changes {
    21  		tags[i] = names.NewFilesystemTag(change)
    22  	}
    23  	alive, dying, dead, err := storageEntityLife(ctx, tags)
    24  	if err != nil {
    25  		return errors.Trace(err)
    26  	}
    27  	logger.Debugf("filesystems alive: %v, dying: %v, dead: %v", alive, dying, dead)
    28  	if len(alive)+len(dying)+len(dead) == 0 {
    29  		return nil
    30  	}
    31  
    32  	// Get filesystem information for filesystems, so we can provision,
    33  	// deprovision, attach and detach.
    34  	filesystemTags := make([]names.FilesystemTag, 0, len(alive)+len(dying)+len(dead))
    35  	for _, tag := range alive {
    36  		filesystemTags = append(filesystemTags, tag.(names.FilesystemTag))
    37  	}
    38  	for _, tag := range dying {
    39  		filesystemTags = append(filesystemTags, tag.(names.FilesystemTag))
    40  	}
    41  	for _, tag := range dead {
    42  		filesystemTags = append(filesystemTags, tag.(names.FilesystemTag))
    43  	}
    44  	filesystemResults, err := ctx.config.Filesystems.Filesystems(filesystemTags)
    45  	if err != nil {
    46  		return errors.Annotatef(err, "getting filesystem information")
    47  	}
    48  
    49  	aliveFilesystemTags := filesystemTags[:len(alive)]
    50  	dyingFilesystemTags := filesystemTags[len(alive) : len(alive)+len(dying)]
    51  	deadFilesystemTags := filesystemTags[len(alive)+len(dying):]
    52  	aliveFilesystemResults := filesystemResults[:len(alive)]
    53  	dyingFilesystemResults := filesystemResults[len(alive) : len(alive)+len(dying)]
    54  	deadFilesystemResults := filesystemResults[len(alive)+len(dying):]
    55  
    56  	if err := processDeadFilesystems(ctx, deadFilesystemTags, deadFilesystemResults); err != nil {
    57  		return errors.Annotate(err, "deprovisioning filesystems")
    58  	}
    59  	if err := processDyingFilesystems(ctx, dyingFilesystemTags, dyingFilesystemResults); err != nil {
    60  		return errors.Annotate(err, "processing dying filesystems")
    61  	}
    62  	if err := processAliveFilesystems(ctx, aliveFilesystemTags, aliveFilesystemResults); err != nil {
    63  		return errors.Annotate(err, "provisioning filesystems")
    64  	}
    65  	return nil
    66  }
    67  
    68  // filesystemAttachmentsChanged is called when the lifecycle states of the filesystem
    69  // attachments with the provided IDs have been seen to have changed.
    70  func filesystemAttachmentsChanged(ctx *context, watcherIds []watcher.MachineStorageId) error {
    71  	ids := copyMachineStorageIds(watcherIds)
    72  	alive, dying, dead, err := attachmentLife(ctx, ids)
    73  	if err != nil {
    74  		return errors.Trace(err)
    75  	}
    76  	logger.Debugf("filesystem attachment alive: %v, dying: %v, dead: %v", alive, dying, dead)
    77  	if len(dead) != 0 {
    78  		// We should not see dead filesystem attachments;
    79  		// attachments go directly from Dying to removed.
    80  		logger.Warningf("unexpected dead filesystem attachments: %v", dead)
    81  	}
    82  	if len(alive)+len(dying) == 0 {
    83  		return nil
    84  	}
    85  
    86  	// Get filesystem information for alive and dying filesystem attachments, so
    87  	// we can attach/detach.
    88  	ids = append(alive, dying...)
    89  	filesystemAttachmentResults, err := ctx.config.Filesystems.FilesystemAttachments(ids)
    90  	if err != nil {
    91  		return errors.Annotatef(err, "getting filesystem attachment information")
    92  	}
    93  
    94  	// Deprovision Dying filesystem attachments.
    95  	dyingFilesystemAttachmentResults := filesystemAttachmentResults[len(alive):]
    96  	if err := processDyingFilesystemAttachments(ctx, dying, dyingFilesystemAttachmentResults); err != nil {
    97  		return errors.Annotate(err, "destroying filesystem attachments")
    98  	}
    99  
   100  	// Provision Alive filesystem attachments.
   101  	aliveFilesystemAttachmentResults := filesystemAttachmentResults[:len(alive)]
   102  	if err := processAliveFilesystemAttachments(ctx, alive, aliveFilesystemAttachmentResults); err != nil {
   103  		return errors.Annotate(err, "creating filesystem attachments")
   104  	}
   105  
   106  	return nil
   107  }
   108  
   109  // processDyingFilesystems processes the FilesystemResults for Dying filesystems,
   110  // removing them from provisioning-pending as necessary.
   111  func processDyingFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error {
   112  	for _, tag := range tags {
   113  		removePendingFilesystem(ctx, tag)
   114  	}
   115  	return nil
   116  }
   117  
   118  func updateFilesystem(ctx *context, info storage.Filesystem) {
   119  	ctx.filesystems[info.Tag] = info
   120  	for id, params := range ctx.incompleteFilesystemAttachmentParams {
   121  		if params.FilesystemId == "" && id.AttachmentTag == info.Tag.String() {
   122  			updatePendingFilesystemAttachment(ctx, id, params)
   123  		}
   124  	}
   125  }
   126  
   127  func updatePendingFilesystem(ctx *context, params storage.FilesystemParams) {
   128  	if params.Volume != (names.VolumeTag{}) {
   129  		// The filesystem is volume-backed: we must watch for
   130  		// the corresponding block device. This will trigger a
   131  		// one-time (for the volume) forced update of block
   132  		// devices. If the block device is not immediately
   133  		// available, then we rely on the watcher. The forced
   134  		// update is necessary in case the block device was
   135  		// added to state already, and we didn't observe it.
   136  		if _, ok := ctx.volumeBlockDevices[params.Volume]; !ok {
   137  			ctx.pendingVolumeBlockDevices.Add(params.Volume)
   138  			ctx.incompleteFilesystemParams[params.Tag] = params
   139  			return
   140  		}
   141  	}
   142  	delete(ctx.incompleteFilesystemParams, params.Tag)
   143  	scheduleOperations(ctx, &createFilesystemOp{args: params})
   144  }
   145  
   146  func removePendingFilesystem(ctx *context, tag names.FilesystemTag) {
   147  	delete(ctx.incompleteFilesystemParams, tag)
   148  	ctx.schedule.Remove(tag)
   149  }
   150  
   151  // updatePendingFilesystemAttachment adds the given filesystem attachment params to
   152  // either the incomplete set or the schedule. If the params are incomplete
   153  // due to a missing instance ID, updatePendingFilesystemAttachment will request
   154  // that the machine be watched so its instance ID can be learned.
   155  func updatePendingFilesystemAttachment(
   156  	ctx *context,
   157  	id params.MachineStorageId,
   158  	params storage.FilesystemAttachmentParams,
   159  ) {
   160  	var incomplete bool
   161  	filesystem, ok := ctx.filesystems[params.Filesystem]
   162  	if !ok {
   163  		incomplete = true
   164  	} else {
   165  		params.FilesystemId = filesystem.FilesystemId
   166  		if filesystem.Volume != (names.VolumeTag{}) {
   167  			// The filesystem is volume-backed: if the filesystem
   168  			// was created in another session, then the block device
   169  			// may not have been seen yet. We must wait for the block
   170  			// device watcher to trigger.
   171  			if _, ok := ctx.volumeBlockDevices[filesystem.Volume]; !ok {
   172  				incomplete = true
   173  			}
   174  		}
   175  	}
   176  	if params.InstanceId == "" {
   177  		watchMachine(ctx, params.Machine)
   178  		incomplete = true
   179  	}
   180  	if params.FilesystemId == "" {
   181  		incomplete = true
   182  	}
   183  	if incomplete {
   184  		ctx.incompleteFilesystemAttachmentParams[id] = params
   185  		return
   186  	}
   187  	delete(ctx.incompleteFilesystemAttachmentParams, id)
   188  	scheduleOperations(ctx, &attachFilesystemOp{args: params})
   189  }
   190  
   191  // removePendingFilesystemAttachment removes the specified pending filesystem
   192  // attachment from the incomplete set and/or the schedule if it exists
   193  // there.
   194  func removePendingFilesystemAttachment(ctx *context, id params.MachineStorageId) {
   195  	delete(ctx.incompleteFilesystemAttachmentParams, id)
   196  	ctx.schedule.Remove(id)
   197  }
   198  
   199  // processDeadFilesystems processes the FilesystemResults for Dead filesystems,
   200  // deprovisioning filesystems and removing from state as necessary.
   201  func processDeadFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error {
   202  	for _, tag := range tags {
   203  		removePendingFilesystem(ctx, tag)
   204  	}
   205  	var destroy []names.FilesystemTag
   206  	var remove []names.Tag
   207  	for i, result := range filesystemResults {
   208  		tag := tags[i]
   209  		if result.Error == nil {
   210  			logger.Debugf("filesystem %s is provisioned, queuing for deprovisioning", tag.Id())
   211  			filesystem, err := filesystemFromParams(result.Result)
   212  			if err != nil {
   213  				return errors.Annotate(err, "getting filesystem info")
   214  			}
   215  			updateFilesystem(ctx, filesystem)
   216  			destroy = append(destroy, tag)
   217  			continue
   218  		}
   219  		if params.IsCodeNotProvisioned(result.Error) {
   220  			logger.Debugf("filesystem %s is not provisioned, queuing for removal", tag.Id())
   221  			remove = append(remove, tag)
   222  			continue
   223  		}
   224  		return errors.Annotatef(result.Error, "getting filesystem information for filesystem %s", tag.Id())
   225  	}
   226  	if len(destroy) > 0 {
   227  		ops := make([]scheduleOp, len(destroy))
   228  		for i, tag := range destroy {
   229  			ops[i] = &destroyFilesystemOp{tag: tag}
   230  		}
   231  		scheduleOperations(ctx, ops...)
   232  	}
   233  	if err := removeEntities(ctx, remove); err != nil {
   234  		return errors.Annotate(err, "removing filesystems from state")
   235  	}
   236  	return nil
   237  }
   238  
   239  // processDyingFilesystemAttachments processes the FilesystemAttachmentResults for
   240  // Dying filesystem attachments, detaching filesystems and updating state as necessary.
   241  func processDyingFilesystemAttachments(
   242  	ctx *context,
   243  	ids []params.MachineStorageId,
   244  	filesystemAttachmentResults []params.FilesystemAttachmentResult,
   245  ) error {
   246  	for _, id := range ids {
   247  		removePendingFilesystemAttachment(ctx, id)
   248  	}
   249  	detach := make([]params.MachineStorageId, 0, len(ids))
   250  	remove := make([]params.MachineStorageId, 0, len(ids))
   251  	for i, result := range filesystemAttachmentResults {
   252  		id := ids[i]
   253  		if result.Error == nil {
   254  			detach = append(detach, id)
   255  			continue
   256  		}
   257  		if params.IsCodeNotProvisioned(result.Error) {
   258  			remove = append(remove, id)
   259  			continue
   260  		}
   261  		return errors.Annotatef(result.Error, "getting information for filesystem attachment %v", id)
   262  	}
   263  	if len(detach) > 0 {
   264  		attachmentParams, err := filesystemAttachmentParams(ctx, detach)
   265  		if err != nil {
   266  			return errors.Trace(err)
   267  		}
   268  		ops := make([]scheduleOp, len(attachmentParams))
   269  		for i, p := range attachmentParams {
   270  			ops[i] = &detachFilesystemOp{args: p}
   271  		}
   272  		scheduleOperations(ctx, ops...)
   273  	}
   274  	if err := removeAttachments(ctx, remove); err != nil {
   275  		return errors.Annotate(err, "removing attachments from state")
   276  	}
   277  	return nil
   278  }
   279  
   280  // processAliveFilesystems processes the FilesystemResults for Alive filesystems,
   281  // provisioning filesystems and setting the info in state as necessary.
   282  func processAliveFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error {
   283  	// Filter out the already-provisioned filesystems.
   284  	pending := make([]names.FilesystemTag, 0, len(tags))
   285  	for i, result := range filesystemResults {
   286  		tag := tags[i]
   287  		if result.Error == nil {
   288  			// Filesystem is already provisioned: skip.
   289  			logger.Debugf("filesystem %q is already provisioned, nothing to do", tag.Id())
   290  			filesystem, err := filesystemFromParams(result.Result)
   291  			if err != nil {
   292  				return errors.Annotate(err, "getting filesystem info")
   293  			}
   294  			updateFilesystem(ctx, filesystem)
   295  			if filesystem.Volume != (names.VolumeTag{}) {
   296  				// Ensure that volume-backed filesystems' block
   297  				// devices are present even after creating the
   298  				// filesystem, so that attachments can be made.
   299  				maybeAddPendingVolumeBlockDevice(ctx, filesystem.Volume)
   300  			}
   301  			continue
   302  		}
   303  		if !params.IsCodeNotProvisioned(result.Error) {
   304  			return errors.Annotatef(
   305  				result.Error, "getting filesystem information for filesystem %q", tag.Id(),
   306  			)
   307  		}
   308  		// The filesystem has not yet been provisioned, so record its tag
   309  		// to enquire about parameters below.
   310  		pending = append(pending, tag)
   311  	}
   312  	if len(pending) == 0 {
   313  		return nil
   314  	}
   315  	params, err := filesystemParams(ctx, pending)
   316  	if err != nil {
   317  		return errors.Annotate(err, "getting filesystem params")
   318  	}
   319  	for _, params := range params {
   320  		updatePendingFilesystem(ctx, params)
   321  	}
   322  	return nil
   323  }
   324  
   325  func maybeAddPendingVolumeBlockDevice(ctx *context, v names.VolumeTag) {
   326  	if _, ok := ctx.volumeBlockDevices[v]; !ok {
   327  		ctx.pendingVolumeBlockDevices.Add(v)
   328  	}
   329  }
   330  
   331  // processAliveFilesystemAttachments processes the FilesystemAttachmentResults
   332  // for Alive filesystem attachments, attaching filesystems and setting the info
   333  // in state as necessary.
   334  func processAliveFilesystemAttachments(
   335  	ctx *context,
   336  	ids []params.MachineStorageId,
   337  	filesystemAttachmentResults []params.FilesystemAttachmentResult,
   338  ) error {
   339  	// Filter out the already-attached.
   340  	pending := make([]params.MachineStorageId, 0, len(ids))
   341  	for i, result := range filesystemAttachmentResults {
   342  		if result.Error == nil {
   343  			// Filesystem attachment is already provisioned: if we
   344  			// didn't (re)attach in this session, then we must do
   345  			// so now.
   346  			action := "nothing to do"
   347  			if _, ok := ctx.filesystemAttachments[ids[i]]; !ok {
   348  				// Not yet (re)attached in this session.
   349  				pending = append(pending, ids[i])
   350  				action = "will reattach"
   351  			}
   352  			logger.Debugf(
   353  				"%s is already attached to %s, %s",
   354  				ids[i].AttachmentTag, ids[i].MachineTag, action,
   355  			)
   356  			removePendingFilesystemAttachment(ctx, ids[i])
   357  			continue
   358  		}
   359  		if !params.IsCodeNotProvisioned(result.Error) {
   360  			return errors.Annotatef(
   361  				result.Error, "getting information for attachment %v", ids[i],
   362  			)
   363  		}
   364  		// The filesystem has not yet been attached, so
   365  		// record its tag to enquire about parameters below.
   366  		pending = append(pending, ids[i])
   367  	}
   368  	if len(pending) == 0 {
   369  		return nil
   370  	}
   371  	params, err := filesystemAttachmentParams(ctx, pending)
   372  	if err != nil {
   373  		return errors.Trace(err)
   374  	}
   375  	for i, params := range params {
   376  		updatePendingFilesystemAttachment(ctx, pending[i], params)
   377  	}
   378  	return nil
   379  }
   380  
   381  // filesystemAttachmentParams obtains the specified attachments' parameters.
   382  func filesystemAttachmentParams(
   383  	ctx *context, ids []params.MachineStorageId,
   384  ) ([]storage.FilesystemAttachmentParams, error) {
   385  	paramsResults, err := ctx.config.Filesystems.FilesystemAttachmentParams(ids)
   386  	if err != nil {
   387  		return nil, errors.Annotate(err, "getting filesystem attachment params")
   388  	}
   389  	attachmentParams := make([]storage.FilesystemAttachmentParams, len(ids))
   390  	for i, result := range paramsResults {
   391  		if result.Error != nil {
   392  			return nil, errors.Annotate(result.Error, "getting filesystem attachment parameters")
   393  		}
   394  		params, err := filesystemAttachmentParamsFromParams(result.Result)
   395  		if err != nil {
   396  			return nil, errors.Annotate(err, "getting filesystem attachment parameters")
   397  		}
   398  		attachmentParams[i] = params
   399  	}
   400  	return attachmentParams, nil
   401  }
   402  
   403  // filesystemParams obtains the specified filesystems' parameters.
   404  func filesystemParams(ctx *context, tags []names.FilesystemTag) ([]storage.FilesystemParams, error) {
   405  	paramsResults, err := ctx.config.Filesystems.FilesystemParams(tags)
   406  	if err != nil {
   407  		return nil, errors.Annotate(err, "getting filesystem params")
   408  	}
   409  	allParams := make([]storage.FilesystemParams, len(tags))
   410  	for i, result := range paramsResults {
   411  		if result.Error != nil {
   412  			return nil, errors.Annotate(result.Error, "getting filesystem parameters")
   413  		}
   414  		params, err := filesystemParamsFromParams(result.Result)
   415  		if err != nil {
   416  			return nil, errors.Annotate(err, "getting filesystem parameters")
   417  		}
   418  		allParams[i] = params
   419  	}
   420  	return allParams, nil
   421  }
   422  
   423  func filesystemFromParams(in params.Filesystem) (storage.Filesystem, error) {
   424  	filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag)
   425  	if err != nil {
   426  		return storage.Filesystem{}, errors.Trace(err)
   427  	}
   428  	var volumeTag names.VolumeTag
   429  	if in.VolumeTag != "" {
   430  		volumeTag, err = names.ParseVolumeTag(in.VolumeTag)
   431  		if err != nil {
   432  			return storage.Filesystem{}, errors.Trace(err)
   433  		}
   434  	}
   435  	return storage.Filesystem{
   436  		filesystemTag,
   437  		volumeTag,
   438  		storage.FilesystemInfo{
   439  			in.Info.FilesystemId,
   440  			in.Info.Size,
   441  		},
   442  	}, nil
   443  }
   444  
   445  func filesystemParamsFromParams(in params.FilesystemParams) (storage.FilesystemParams, error) {
   446  	filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag)
   447  	if err != nil {
   448  		return storage.FilesystemParams{}, errors.Trace(err)
   449  	}
   450  	var volumeTag names.VolumeTag
   451  	if in.VolumeTag != "" {
   452  		volumeTag, err = names.ParseVolumeTag(in.VolumeTag)
   453  		if err != nil {
   454  			return storage.FilesystemParams{}, errors.Trace(err)
   455  		}
   456  	}
   457  	providerType := storage.ProviderType(in.Provider)
   458  	return storage.FilesystemParams{
   459  		filesystemTag,
   460  		volumeTag,
   461  		in.Size,
   462  		providerType,
   463  		in.Attributes,
   464  		in.Tags,
   465  	}, nil
   466  }
   467  
   468  func filesystemAttachmentParamsFromParams(in params.FilesystemAttachmentParams) (storage.FilesystemAttachmentParams, error) {
   469  	machineTag, err := names.ParseMachineTag(in.MachineTag)
   470  	if err != nil {
   471  		return storage.FilesystemAttachmentParams{}, errors.Trace(err)
   472  	}
   473  	filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag)
   474  	if err != nil {
   475  		return storage.FilesystemAttachmentParams{}, errors.Trace(err)
   476  	}
   477  	return storage.FilesystemAttachmentParams{
   478  		AttachmentParams: storage.AttachmentParams{
   479  			Provider:   storage.ProviderType(in.Provider),
   480  			Machine:    machineTag,
   481  			InstanceId: instance.Id(in.InstanceId),
   482  			ReadOnly:   in.ReadOnly,
   483  		},
   484  		Filesystem:   filesystemTag,
   485  		FilesystemId: in.FilesystemId,
   486  		Path:         in.MountPoint,
   487  	}, nil
   488  }