github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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  	"github.com/juju/names"
     9  	"github.com/juju/utils/set"
    10  
    11  	"github.com/juju/juju/apiserver/params"
    12  )
    13  
    14  // machineBlockDevicesChanged is called when the block devices of the scoped
    15  // machine have been seen to have changed. This triggers a refresh of all
    16  // block devices for attached volumes backing pending filesystems.
    17  func machineBlockDevicesChanged(ctx *context) error {
    18  	if len(ctx.pendingFilesystems) == 0 {
    19  		return nil
    20  	}
    21  	volumeTags := make([]names.VolumeTag, 0, len(ctx.pendingFilesystems))
    22  	for _, params := range ctx.pendingFilesystems {
    23  		if params.Volume == (names.VolumeTag{}) {
    24  			// Filesystem is not volume-backed.
    25  			continue
    26  		}
    27  		if _, ok := ctx.volumeBlockDevices[params.Volume]; ok {
    28  			// Backing-volume's block device is already attached.
    29  			continue
    30  		}
    31  		volumeTags = append(volumeTags, params.Volume)
    32  	}
    33  	if len(volumeTags) == 0 {
    34  		return nil
    35  	}
    36  	return refreshVolumeBlockDevices(ctx, volumeTags)
    37  }
    38  
    39  // processPendingVolumeBlockDevices is called before waiting for any events,
    40  // to force a block-device query for any volumes for which we have not
    41  // previously observed block devices.
    42  func processPendingVolumeBlockDevices(ctx *context) error {
    43  	if len(ctx.pendingVolumeBlockDevices) == 0 {
    44  		logger.Tracef("no pending volume block devices")
    45  		return nil
    46  	}
    47  	volumeTags := make([]names.VolumeTag, len(ctx.pendingVolumeBlockDevices))
    48  	for i, tag := range ctx.pendingVolumeBlockDevices.SortedValues() {
    49  		volumeTags[i] = tag.(names.VolumeTag)
    50  	}
    51  	// Clear out the pending set, so we don't force-refresh again.
    52  	ctx.pendingVolumeBlockDevices = set.NewTags()
    53  	return refreshVolumeBlockDevices(ctx, volumeTags)
    54  }
    55  
    56  // refreshVolumeBlockDevices refreshes the block devices for the specified
    57  // volumes.
    58  func refreshVolumeBlockDevices(ctx *context, volumeTags []names.VolumeTag) error {
    59  	machineTag, ok := ctx.scope.(names.MachineTag)
    60  	if !ok {
    61  		// This function should only be called by machine-scoped
    62  		// storage provisioners.
    63  		panic(errors.New("expected machine tag"))
    64  	}
    65  	ids := make([]params.MachineStorageId, len(volumeTags))
    66  	for i, volumeTag := range volumeTags {
    67  		ids[i] = params.MachineStorageId{
    68  			MachineTag:    machineTag.String(),
    69  			AttachmentTag: volumeTag.String(),
    70  		}
    71  	}
    72  	results, err := ctx.volumeAccessor.VolumeBlockDevices(ids)
    73  	if err != nil {
    74  		return errors.Annotate(err, "refreshing volume block devices")
    75  	}
    76  	for i, result := range results {
    77  		if result.Error == nil {
    78  			ctx.volumeBlockDevices[volumeTags[i]] = result.Result
    79  		} else if params.IsCodeNotProvisioned(result.Error) || params.IsCodeNotFound(result.Error) {
    80  			// Either the volume (attachment) isn't provisioned,
    81  			// or the corresponding block device is not yet known.
    82  			//
    83  			// Neither of these errors is fatal; we just wait for
    84  			// the block device watcher to notify us again.
    85  		} else {
    86  			return errors.Annotatef(
    87  				err, "getting block device info for volume attachment %v",
    88  				ids[i],
    89  			)
    90  		}
    91  	}
    92  	return nil
    93  }