github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/storageprovisioner/blockdevices.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  )
    12  
    13  // machineBlockDevicesChanged is called when the block devices of the scoped
    14  // machine have been seen to have changed. This triggers a refresh of all
    15  // block devices for attached volumes backing pending filesystems.
    16  func machineBlockDevicesChanged(ctx *context) error {
    17  	volumeTags := make([]names.VolumeTag, 0, len(ctx.incompleteFilesystemParams))
    18  	// We must query volumes for both incomplete filesystems
    19  	// and incomplete filesystem attachments, because even
    20  	// though a filesystem attachment cannot exist without a
    21  	// filesystem, the filesystem may be created and attached
    22  	// in different sessions, and there is no guarantee that
    23  	// the block device will remain attached to the machine
    24  	// in between.
    25  	for _, params := range ctx.incompleteFilesystemParams {
    26  		if params.Volume == (names.VolumeTag{}) {
    27  			// Filesystem is not volume-backed.
    28  			continue
    29  		}
    30  		if _, ok := ctx.volumeBlockDevices[params.Volume]; ok {
    31  			// Backing-volume's block device is already attached.
    32  			continue
    33  		}
    34  		volumeTags = append(volumeTags, params.Volume)
    35  	}
    36  	for _, params := range ctx.incompleteFilesystemAttachmentParams {
    37  		filesystem, ok := ctx.filesystems[params.Filesystem]
    38  		if !ok {
    39  			continue
    40  		}
    41  		if filesystem.Volume == (names.VolumeTag{}) {
    42  			// Filesystem is not volume-backed.
    43  			continue
    44  		}
    45  		if _, ok := ctx.volumeBlockDevices[filesystem.Volume]; ok {
    46  			// Backing-volume's block device is already attached.
    47  			continue
    48  		}
    49  		var found bool
    50  		for _, tag := range volumeTags {
    51  			if filesystem.Volume == tag {
    52  				found = true
    53  				break
    54  			}
    55  		}
    56  		if !found {
    57  			volumeTags = append(volumeTags, filesystem.Volume)
    58  		}
    59  	}
    60  	if len(volumeTags) == 0 {
    61  		return nil
    62  	}
    63  	return refreshVolumeBlockDevices(ctx, volumeTags)
    64  }
    65  
    66  // processPendingVolumeBlockDevices is called before waiting for any events,
    67  // to force a block-device query for any volumes for which we have not
    68  // previously observed block devices.
    69  func processPendingVolumeBlockDevices(ctx *context) error {
    70  	if len(ctx.pendingVolumeBlockDevices) == 0 {
    71  		logger.Tracef("no pending volume block devices")
    72  		return nil
    73  	}
    74  	volumeTags := make([]names.VolumeTag, len(ctx.pendingVolumeBlockDevices))
    75  	for i, tag := range ctx.pendingVolumeBlockDevices.SortedValues() {
    76  		volumeTags[i] = tag.(names.VolumeTag)
    77  	}
    78  	// Clear out the pending set, so we don't force-refresh again.
    79  	ctx.pendingVolumeBlockDevices = names.NewSet()
    80  	return refreshVolumeBlockDevices(ctx, volumeTags)
    81  }
    82  
    83  // refreshVolumeBlockDevices refreshes the block devices for the specified
    84  // volumes.
    85  func refreshVolumeBlockDevices(ctx *context, volumeTags []names.VolumeTag) error {
    86  	machineTag, ok := ctx.config.Scope.(names.MachineTag)
    87  	if !ok {
    88  		// This function should only be called by machine-scoped
    89  		// storage provisioners.
    90  		logger.Warningf("refresh block devices, expected machine tag, got %v", ctx.config.Scope)
    91  		return nil
    92  	}
    93  	ids := make([]params.MachineStorageId, len(volumeTags))
    94  	for i, volumeTag := range volumeTags {
    95  		ids[i] = params.MachineStorageId{
    96  			MachineTag:    machineTag.String(),
    97  			AttachmentTag: volumeTag.String(),
    98  		}
    99  	}
   100  	results, err := ctx.config.Volumes.VolumeBlockDevices(ids)
   101  	if err != nil {
   102  		return errors.Annotate(err, "refreshing volume block devices")
   103  	}
   104  	for i, result := range results {
   105  		if result.Error == nil {
   106  			ctx.volumeBlockDevices[volumeTags[i]] = result.Result
   107  			for _, params := range ctx.incompleteFilesystemParams {
   108  				if params.Volume == volumeTags[i] {
   109  					updatePendingFilesystem(ctx, params)
   110  				}
   111  			}
   112  			for id, params := range ctx.incompleteFilesystemAttachmentParams {
   113  				filesystem, ok := ctx.filesystems[params.Filesystem]
   114  				if !ok {
   115  					continue
   116  				}
   117  				if filesystem.Volume == volumeTags[i] {
   118  					updatePendingFilesystemAttachment(ctx, id, params)
   119  				}
   120  			}
   121  		} else if params.IsCodeNotProvisioned(result.Error) || params.IsCodeNotFound(result.Error) {
   122  			// Either the volume (attachment) isn't provisioned,
   123  			// or the corresponding block device is not yet known.
   124  			//
   125  			// Neither of these errors is fatal; we just wait for
   126  			// the block device watcher to notify us again.
   127  		} else {
   128  			return errors.Annotatef(
   129  				err, "getting block device info for volume attachment %v",
   130  				ids[i],
   131  			)
   132  		}
   133  	}
   134  	return nil
   135  }