github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/storageprovisioner/volumes.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  
    10  	"github.com/juju/juju/apiserver/params"
    11  	"github.com/juju/juju/environs/config"
    12  	"github.com/juju/juju/instance"
    13  	"github.com/juju/juju/storage"
    14  )
    15  
    16  // volumesChanged is called when the lifecycle states of the volumes
    17  // with the provided IDs have been seen to have changed.
    18  func volumesChanged(ctx *context, changes []string) error {
    19  	tags := make([]names.Tag, len(changes))
    20  	for i, change := range changes {
    21  		tags[i] = names.NewVolumeTag(change)
    22  	}
    23  	alive, dying, dead, err := storageEntityLife(ctx, tags)
    24  	if err != nil {
    25  		return errors.Trace(err)
    26  	}
    27  	logger.Debugf("volumes alive: %v, dying: %v, dead: %v", alive, dying, dead)
    28  	if err := processDyingVolumes(ctx, dying); err != nil {
    29  		return errors.Annotate(err, "processing dying volumes")
    30  	}
    31  	if len(alive)+len(dead) == 0 {
    32  		return nil
    33  	}
    34  
    35  	// Get volume information for alive and dead volumes, so
    36  	// we can provision/deprovision.
    37  	volumeTags := make([]names.VolumeTag, 0, len(alive)+len(dead))
    38  	for _, tag := range alive {
    39  		volumeTags = append(volumeTags, tag.(names.VolumeTag))
    40  	}
    41  	for _, tag := range dead {
    42  		volumeTags = append(volumeTags, tag.(names.VolumeTag))
    43  	}
    44  	volumeResults, err := ctx.volumeAccessor.Volumes(volumeTags)
    45  	if err != nil {
    46  		return errors.Annotatef(err, "getting volume information")
    47  	}
    48  	if err := processDeadVolumes(ctx, volumeTags[len(alive):], volumeResults[len(alive):]); err != nil {
    49  		return errors.Annotate(err, "deprovisioning volumes")
    50  	}
    51  	if err := processAliveVolumes(ctx, alive, volumeResults[:len(alive)]); err != nil {
    52  		return errors.Annotate(err, "provisioning volumes")
    53  	}
    54  	return nil
    55  }
    56  
    57  // volumeAttachmentsChanged is called when the lifecycle states of the volume
    58  // attachments with the provided IDs have been seen to have changed.
    59  func volumeAttachmentsChanged(ctx *context, ids []params.MachineStorageId) error {
    60  	alive, dying, dead, err := attachmentLife(ctx, ids)
    61  	if err != nil {
    62  		return errors.Trace(err)
    63  	}
    64  	logger.Debugf("volume attachments alive: %v, dying: %v, dead: %v", alive, dying, dead)
    65  	if len(dead) != 0 {
    66  		// We should not see dead volume attachments;
    67  		// attachments go directly from Dying to removed.
    68  		logger.Debugf("unexpected dead volume attachments: %v", dead)
    69  	}
    70  	if len(alive)+len(dying) == 0 {
    71  		return nil
    72  	}
    73  
    74  	// Get volume information for alive and dying volume attachments, so
    75  	// we can attach/detach.
    76  	ids = append(alive, dying...)
    77  	volumeAttachmentResults, err := ctx.volumeAccessor.VolumeAttachments(ids)
    78  	if err != nil {
    79  		return errors.Annotatef(err, "getting volume attachment information")
    80  	}
    81  
    82  	// Deprovision Dying volume attachments.
    83  	dyingVolumeAttachmentResults := volumeAttachmentResults[len(alive):]
    84  	if err := processDyingVolumeAttachments(ctx, dying, dyingVolumeAttachmentResults); err != nil {
    85  		return errors.Annotate(err, "deprovisioning volume attachments")
    86  	}
    87  
    88  	// Provision Alive volume attachments.
    89  	aliveVolumeAttachmentResults := volumeAttachmentResults[:len(alive)]
    90  	if err := processAliveVolumeAttachments(ctx, alive, aliveVolumeAttachmentResults); err != nil {
    91  		return errors.Annotate(err, "provisioning volumes")
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  // processDyingVolumes processes the VolumeResults for Dying volumes,
    98  // removing them from provisioning-pending as necessary.
    99  func processDyingVolumes(ctx *context, tags []names.Tag) error {
   100  	for _, tag := range tags {
   101  		delete(ctx.pendingVolumes, tag.(names.VolumeTag))
   102  	}
   103  	return nil
   104  }
   105  
   106  // processDeadVolumes processes the VolumeResults for Dead volumes,
   107  // deprovisioning volumes and removing from state as necessary.
   108  func processDeadVolumes(ctx *context, tags []names.VolumeTag, volumeResults []params.VolumeResult) error {
   109  	for _, tag := range tags {
   110  		delete(ctx.pendingVolumes, tag)
   111  	}
   112  	var destroy []names.VolumeTag
   113  	var remove []names.Tag
   114  	for i, result := range volumeResults {
   115  		tag := tags[i]
   116  		if result.Error == nil {
   117  			logger.Debugf("volume %s is provisioned, queuing for deprovisioning", tag.Id())
   118  			volume, err := volumeFromParams(result.Result)
   119  			if err != nil {
   120  				return errors.Annotate(err, "getting volume info")
   121  			}
   122  			ctx.volumes[tag] = volume
   123  			destroy = append(destroy, tag)
   124  			continue
   125  		}
   126  		if params.IsCodeNotProvisioned(result.Error) {
   127  			logger.Debugf("volume %s is not provisioned, queuing for removal", tag.Id())
   128  			remove = append(remove, tag)
   129  			continue
   130  		}
   131  		return errors.Annotatef(result.Error, "getting volume information for volume %s", tag.Id())
   132  	}
   133  	if len(destroy)+len(remove) == 0 {
   134  		return nil
   135  	}
   136  	if len(destroy) > 0 {
   137  		errorResults, err := destroyVolumes(ctx, destroy)
   138  		if err != nil {
   139  			return errors.Annotate(err, "destroying volumes")
   140  		}
   141  		for i, tag := range destroy {
   142  			if err := errorResults[i]; err != nil {
   143  				return errors.Annotatef(err, "destroying %s", names.ReadableString(tag))
   144  			}
   145  			remove = append(remove, tag)
   146  		}
   147  	}
   148  	if err := removeEntities(ctx, remove); err != nil {
   149  		return errors.Annotate(err, "removing volumes from state")
   150  	}
   151  	return nil
   152  }
   153  
   154  // processDyingVolumeAttachments processes the VolumeAttachmentResults for
   155  // Dying volume attachments, detaching volumes and updating state as necessary.
   156  func processDyingVolumeAttachments(
   157  	ctx *context,
   158  	ids []params.MachineStorageId,
   159  	volumeAttachmentResults []params.VolumeAttachmentResult,
   160  ) error {
   161  	if len(ids) == 0 {
   162  		return nil
   163  	}
   164  	for _, id := range ids {
   165  		delete(ctx.pendingVolumeAttachments, id)
   166  	}
   167  	detach := make([]params.MachineStorageId, 0, len(ids))
   168  	remove := make([]params.MachineStorageId, 0, len(ids))
   169  	for i, result := range volumeAttachmentResults {
   170  		id := ids[i]
   171  		if result.Error == nil {
   172  			detach = append(detach, id)
   173  			continue
   174  		}
   175  		if params.IsCodeNotProvisioned(result.Error) {
   176  			remove = append(remove, id)
   177  			continue
   178  		}
   179  		return errors.Annotatef(result.Error, "getting information for volume attachment %v", id)
   180  	}
   181  	if len(detach) > 0 {
   182  		attachmentParams, err := volumeAttachmentParams(ctx, detach)
   183  		if err != nil {
   184  			return errors.Trace(err)
   185  		}
   186  		if err := detachVolumes(ctx, attachmentParams); err != nil {
   187  			return errors.Annotate(err, "detaching volumes")
   188  		}
   189  		remove = append(remove, detach...)
   190  	}
   191  	if err := removeAttachments(ctx, remove); err != nil {
   192  		return errors.Annotate(err, "removing attachments from state")
   193  	}
   194  	return nil
   195  }
   196  
   197  // processAliveVolumes processes the VolumeResults for Alive volumes,
   198  // provisioning volumes and setting the info in state as necessary.
   199  func processAliveVolumes(ctx *context, tags []names.Tag, volumeResults []params.VolumeResult) error {
   200  	// Filter out the already-provisioned volumes.
   201  	pending := make([]names.VolumeTag, 0, len(tags))
   202  	for i, result := range volumeResults {
   203  		volumeTag := tags[i].(names.VolumeTag)
   204  		if result.Error == nil {
   205  			// Volume is already provisioned: skip.
   206  			logger.Debugf("volume %q is already provisioned, nothing to do", tags[i].Id())
   207  			volume, err := volumeFromParams(result.Result)
   208  			if err != nil {
   209  				return errors.Annotate(err, "getting volume info")
   210  			}
   211  			ctx.volumes[volumeTag] = volume
   212  			delete(ctx.pendingVolumes, volumeTag)
   213  			continue
   214  		}
   215  		if !params.IsCodeNotProvisioned(result.Error) {
   216  			return errors.Annotatef(
   217  				result.Error, "getting volume information for volume %q", tags[i].Id(),
   218  			)
   219  		}
   220  		// The volume has not yet been provisioned, so record its tag
   221  		// to enquire about parameters below.
   222  		pending = append(pending, volumeTag)
   223  	}
   224  	if len(pending) == 0 {
   225  		return nil
   226  	}
   227  	volumeParams, err := volumeParams(ctx, pending)
   228  	if err != nil {
   229  		return errors.Annotate(err, "getting volume params")
   230  	}
   231  	for i, params := range volumeParams {
   232  		if params.Attachment.InstanceId == "" {
   233  			watchMachine(ctx, params.Attachment.Machine)
   234  		}
   235  		ctx.pendingVolumes[pending[i]] = params
   236  	}
   237  	return nil
   238  }
   239  
   240  // processPendingVolumes creates as many of the pending volumes as possible,
   241  // first ensuring that their prerequisites have been met.
   242  func processPendingVolumes(ctx *context) error {
   243  	if len(ctx.pendingVolumes) == 0 {
   244  		logger.Tracef("no pending volumes")
   245  		return nil
   246  	}
   247  	ready := make([]storage.VolumeParams, 0, len(ctx.pendingVolumes))
   248  	for tag, volumeParams := range ctx.pendingVolumes {
   249  		if volumeParams.Attachment.InstanceId == "" {
   250  			logger.Debugf("machine %v has not been provisioned yet", volumeParams.Attachment.Machine.Id())
   251  			continue
   252  		}
   253  		ready = append(ready, volumeParams)
   254  		delete(ctx.pendingVolumes, tag)
   255  	}
   256  	if len(ready) == 0 {
   257  		return nil
   258  	}
   259  	volumes, volumeAttachments, err := createVolumes(ctx.environConfig, ctx.storageDir, ready)
   260  	if err != nil {
   261  		return errors.Annotate(err, "creating volumes")
   262  	}
   263  	if len(volumes) == 0 {
   264  		return nil
   265  	}
   266  	// TODO(axw) we need to be able to list volumes in the provider,
   267  	// by environment, so that we can "harvest" them if they're
   268  	// unknown. This will take care of killing volumes that we fail
   269  	// to record in state.
   270  	errorResults, err := ctx.volumeAccessor.SetVolumeInfo(volumesFromStorage(volumes))
   271  	if err != nil {
   272  		return errors.Annotate(err, "publishing volumes to state")
   273  	}
   274  	for i, result := range errorResults {
   275  		if result.Error != nil {
   276  			return errors.Annotatef(
   277  				result.Error, "publishing volume %s to state",
   278  				volumes[i].Tag.Id(),
   279  			)
   280  		}
   281  	}
   282  	for _, v := range volumes {
   283  		ctx.volumes[v.Tag] = v
   284  	}
   285  	// Note: the storage provisioner that creates a volume is also
   286  	// responsible for creating the volume attachment. It is therefore
   287  	// safe to set the volume attachment info after the volume info,
   288  	// without leading to the possibility of concurrent, duplicate
   289  	// attachments.
   290  	err = setVolumeAttachmentInfo(ctx, volumeAttachments)
   291  	if err != nil {
   292  		return errors.Trace(err)
   293  	}
   294  	return nil
   295  }
   296  
   297  // processAliveVolumeAttachments processes the VolumeAttachmentResults
   298  // for Alive volume attachments, attaching volumes and setting the info
   299  // in state as necessary.
   300  func processAliveVolumeAttachments(
   301  	ctx *context,
   302  	ids []params.MachineStorageId,
   303  	volumeAttachmentResults []params.VolumeAttachmentResult,
   304  ) error {
   305  	// Filter out the already-attached.
   306  	pending := make([]params.MachineStorageId, 0, len(ids))
   307  	for i, result := range volumeAttachmentResults {
   308  		if result.Error == nil {
   309  			delete(ctx.pendingVolumeAttachments, ids[i])
   310  			// Volume attachment is already provisioned: if we
   311  			// didn't (re)attach in this session, then we must
   312  			// do so now.
   313  			action := "nothing to do"
   314  			if _, ok := ctx.volumeAttachments[ids[i]]; !ok {
   315  				// Not yet (re)attached in this session.
   316  				pending = append(pending, ids[i])
   317  				action = "will reattach"
   318  			}
   319  			logger.Debugf(
   320  				"%s is already attached to %s, %s",
   321  				ids[i].AttachmentTag, ids[i].MachineTag, action,
   322  			)
   323  			continue
   324  		}
   325  		if !params.IsCodeNotProvisioned(result.Error) {
   326  			return errors.Annotatef(
   327  				result.Error, "getting information for attachment %v", ids[i],
   328  			)
   329  		}
   330  		// The volume has not yet been provisioned, so record its tag
   331  		// to enquire about parameters below.
   332  		pending = append(pending, ids[i])
   333  	}
   334  	if len(pending) == 0 {
   335  		return nil
   336  	}
   337  	params, err := volumeAttachmentParams(ctx, pending)
   338  	if err != nil {
   339  		return errors.Trace(err)
   340  	}
   341  	for i, params := range params {
   342  		if params.InstanceId == "" {
   343  			watchMachine(ctx, params.Machine)
   344  		}
   345  		ctx.pendingVolumeAttachments[pending[i]] = params
   346  	}
   347  	return nil
   348  }
   349  
   350  // volumeAttachmentParams obtains the specified attachments' parameters.
   351  func volumeAttachmentParams(
   352  	ctx *context, ids []params.MachineStorageId,
   353  ) ([]storage.VolumeAttachmentParams, error) {
   354  	paramsResults, err := ctx.volumeAccessor.VolumeAttachmentParams(ids)
   355  	if err != nil {
   356  		return nil, errors.Annotate(err, "getting volume attachment params")
   357  	}
   358  	attachmentParams := make([]storage.VolumeAttachmentParams, len(ids))
   359  	for i, result := range paramsResults {
   360  		if result.Error != nil {
   361  			return nil, errors.Annotate(result.Error, "getting volume attachment parameters")
   362  		}
   363  		params, err := volumeAttachmentParamsFromParams(result.Result)
   364  		if err != nil {
   365  			return nil, errors.Annotate(err, "getting volume attachment parameters")
   366  		}
   367  		attachmentParams[i] = params
   368  	}
   369  	return attachmentParams, nil
   370  }
   371  
   372  // processPendingVolumeAttachments creates as many of the pending volume
   373  // attachments as possible, first ensuring that their prerequisites have
   374  // been met.
   375  func processPendingVolumeAttachments(ctx *context) error {
   376  	if len(ctx.pendingVolumeAttachments) == 0 {
   377  		logger.Tracef("no pending volume attachments")
   378  		return nil
   379  	}
   380  	ready := make([]storage.VolumeAttachmentParams, 0, len(ctx.pendingVolumeAttachments))
   381  	for id, params := range ctx.pendingVolumeAttachments {
   382  		volume, ok := ctx.volumes[params.Volume]
   383  		if !ok {
   384  			// volume hasn't been provisioned yet
   385  			logger.Debugf("volume %v has not been provisioned yet", params.Volume.Id())
   386  			continue
   387  		}
   388  		if params.InstanceId == "" {
   389  			logger.Debugf("machine %v has not been provisioned yet", params.Machine.Id())
   390  			continue
   391  		}
   392  		params.VolumeId = volume.VolumeId
   393  		ready = append(ready, params)
   394  		delete(ctx.pendingVolumeAttachments, id)
   395  	}
   396  	if len(ready) == 0 {
   397  		return nil
   398  	}
   399  	volumeAttachments, err := createVolumeAttachments(ctx.environConfig, ctx.storageDir, ready)
   400  	if err != nil {
   401  		return errors.Annotate(err, "creating volume attachments")
   402  	}
   403  	if err := setVolumeAttachmentInfo(ctx, volumeAttachments); err != nil {
   404  		return errors.Trace(err)
   405  	}
   406  	return nil
   407  }
   408  
   409  // createVolumes creates volumes with the specified parameters.
   410  func createVolumes(
   411  	environConfig *config.Config,
   412  	baseStorageDir string,
   413  	params []storage.VolumeParams,
   414  ) ([]storage.Volume, []storage.VolumeAttachment, error) {
   415  	paramsBySource, volumeSources, err := volumeParamsBySource(
   416  		environConfig, baseStorageDir, params,
   417  	)
   418  	if err != nil {
   419  		return nil, nil, errors.Trace(err)
   420  	}
   421  	var allVolumes []storage.Volume
   422  	var allVolumeAttachments []storage.VolumeAttachment
   423  	for sourceName, params := range paramsBySource {
   424  		logger.Debugf("creating volumes: %v", params)
   425  		volumeSource := volumeSources[sourceName]
   426  		volumes, volumeAttachments, err := volumeSource.CreateVolumes(params)
   427  		if err != nil {
   428  			return nil, nil, errors.Annotatef(err, "creating volumes from source %q", sourceName)
   429  		}
   430  		allVolumes = append(allVolumes, volumes...)
   431  		allVolumeAttachments = append(allVolumeAttachments, volumeAttachments...)
   432  	}
   433  	return allVolumes, allVolumeAttachments, nil
   434  }
   435  
   436  // createVolumeAttachments creates volume attachments with the specified parameters.
   437  func createVolumeAttachments(
   438  	environConfig *config.Config,
   439  	baseStorageDir string,
   440  	params []storage.VolumeAttachmentParams,
   441  ) ([]storage.VolumeAttachment, error) {
   442  	paramsBySource, volumeSources, err := volumeAttachmentParamsBySource(
   443  		environConfig, baseStorageDir, params,
   444  	)
   445  	if err != nil {
   446  		return nil, errors.Trace(err)
   447  	}
   448  	var allVolumeAttachments []storage.VolumeAttachment
   449  	for sourceName, params := range paramsBySource {
   450  		logger.Debugf("attaching volumes: %v", params)
   451  		volumeSource := volumeSources[sourceName]
   452  		volumeAttachments, err := volumeSource.AttachVolumes(params)
   453  		if err != nil {
   454  			return nil, errors.Annotatef(err, "attaching volumes from source %q", sourceName)
   455  		}
   456  		allVolumeAttachments = append(allVolumeAttachments, volumeAttachments...)
   457  	}
   458  	return allVolumeAttachments, nil
   459  }
   460  
   461  func setVolumeAttachmentInfo(ctx *context, volumeAttachments []storage.VolumeAttachment) error {
   462  	if len(volumeAttachments) == 0 {
   463  		return nil
   464  	}
   465  	// TODO(axw) we need to be able to list volume attachments in the
   466  	// provider, by environment, so that we can "harvest" them if they're
   467  	// unknown. This will take care of killing volumes that we fail to
   468  	// record in state.
   469  	errorResults, err := ctx.volumeAccessor.SetVolumeAttachmentInfo(
   470  		volumeAttachmentsFromStorage(volumeAttachments),
   471  	)
   472  	if err != nil {
   473  		return errors.Annotate(err, "publishing volumes to state")
   474  	}
   475  	for i, result := range errorResults {
   476  		if result.Error != nil {
   477  			return errors.Annotatef(
   478  				result.Error, "publishing attachment of %s to %s to state",
   479  				names.ReadableString(volumeAttachments[i].Volume),
   480  				names.ReadableString(volumeAttachments[i].Machine),
   481  			)
   482  		}
   483  		// Record the volume attachment in the context.
   484  		ctx.volumeAttachments[params.MachineStorageId{
   485  			MachineTag:    volumeAttachments[i].Machine.String(),
   486  			AttachmentTag: volumeAttachments[i].Volume.String(),
   487  		}] = volumeAttachments[i]
   488  	}
   489  	return nil
   490  }
   491  
   492  func destroyVolumes(ctx *context, tags []names.VolumeTag) ([]error, error) {
   493  	volumeParams, err := volumeParams(ctx, tags)
   494  	if err != nil {
   495  		return nil, errors.Trace(err)
   496  	}
   497  	paramsBySource, volumeSources, err := volumeParamsBySource(
   498  		ctx.environConfig, ctx.storageDir, volumeParams,
   499  	)
   500  	if err != nil {
   501  		return nil, errors.Trace(err)
   502  	}
   503  	var errs []error
   504  	for sourceName, params := range paramsBySource {
   505  		logger.Debugf("destroying volumes from %q: %v", sourceName, params)
   506  		volumeSource := volumeSources[sourceName]
   507  		volumeIds := make([]string, len(params))
   508  		for i, params := range params {
   509  			volume, ok := ctx.volumes[params.Tag]
   510  			if !ok {
   511  				return nil, errors.NotFoundf("volume %s", params.Tag.Id())
   512  			}
   513  			volumeIds[i] = volume.VolumeId
   514  		}
   515  		errs = append(errs, volumeSource.DestroyVolumes(volumeIds)...)
   516  	}
   517  	return errs, nil
   518  }
   519  
   520  // volumeParams obtains the specified volumes' parameters.
   521  func volumeParams(ctx *context, tags []names.VolumeTag) ([]storage.VolumeParams, error) {
   522  	paramsResults, err := ctx.volumeAccessor.VolumeParams(tags)
   523  	if err != nil {
   524  		return nil, errors.Annotate(err, "getting volume params")
   525  	}
   526  	allParams := make([]storage.VolumeParams, len(tags))
   527  	for i, result := range paramsResults {
   528  		if result.Error != nil {
   529  			return nil, errors.Annotate(result.Error, "getting volume parameters")
   530  		}
   531  		params, err := volumeParamsFromParams(result.Result)
   532  		if err != nil {
   533  			return nil, errors.Annotate(err, "getting volume parameters")
   534  		}
   535  		allParams[i] = params
   536  	}
   537  	return allParams, nil
   538  }
   539  
   540  func volumeParamsBySource(
   541  	environConfig *config.Config,
   542  	baseStorageDir string,
   543  	params []storage.VolumeParams,
   544  ) (map[string][]storage.VolumeParams, map[string]storage.VolumeSource, error) {
   545  	// TODO(axw) later we may have multiple instantiations (sources)
   546  	// for a storage provider, e.g. multiple Ceph installations. For
   547  	// now we assume a single source for each provider type, with no
   548  	// configuration.
   549  	volumeSources := make(map[string]storage.VolumeSource)
   550  	for _, params := range params {
   551  		sourceName := string(params.Provider)
   552  		if _, ok := volumeSources[sourceName]; ok {
   553  			continue
   554  		}
   555  		volumeSource, err := volumeSource(
   556  			environConfig, baseStorageDir, sourceName, params.Provider,
   557  		)
   558  		if errors.Cause(err) == errNonDynamic {
   559  			volumeSource = nil
   560  		} else if err != nil {
   561  			return nil, nil, errors.Annotate(err, "getting volume source")
   562  		}
   563  		volumeSources[sourceName] = volumeSource
   564  	}
   565  	paramsBySource := make(map[string][]storage.VolumeParams)
   566  	for _, params := range params {
   567  		sourceName := string(params.Provider)
   568  		volumeSource := volumeSources[sourceName]
   569  		if volumeSource == nil {
   570  			// Ignore nil volume sources; this means that the
   571  			// volume should be created by the machine-provisioner.
   572  			continue
   573  		}
   574  		err := volumeSource.ValidateVolumeParams(params)
   575  		switch errors.Cause(err) {
   576  		case nil:
   577  			paramsBySource[sourceName] = append(paramsBySource[sourceName], params)
   578  		default:
   579  			return nil, nil, errors.Annotatef(err, "invalid parameters for volume %s", params.Tag.Id())
   580  		}
   581  	}
   582  	return paramsBySource, volumeSources, nil
   583  }
   584  
   585  func detachVolumes(ctx *context, attachments []storage.VolumeAttachmentParams) error {
   586  	paramsBySource, volumeSources, err := volumeAttachmentParamsBySource(
   587  		ctx.environConfig, ctx.storageDir, attachments,
   588  	)
   589  	if err != nil {
   590  		return errors.Trace(err)
   591  	}
   592  	for sourceName, params := range paramsBySource {
   593  		logger.Debugf("detaching volumes: %v", params)
   594  		volumeSource := volumeSources[sourceName]
   595  		if err := volumeSource.DetachVolumes(params); err != nil {
   596  			return errors.Annotatef(err, "detaching volumes from source %q", sourceName)
   597  		}
   598  	}
   599  	return nil
   600  }
   601  
   602  func volumeAttachmentParamsBySource(
   603  	environConfig *config.Config,
   604  	baseStorageDir string,
   605  	params []storage.VolumeAttachmentParams,
   606  ) (map[string][]storage.VolumeAttachmentParams, map[string]storage.VolumeSource, error) {
   607  	// TODO(axw) later we may have multiple instantiations (sources)
   608  	// for a storage provider, e.g. multiple Ceph installations. For
   609  	// now we assume a single source for each provider type, with no
   610  	// configuration.
   611  	volumeSources := make(map[string]storage.VolumeSource)
   612  	paramsBySource := make(map[string][]storage.VolumeAttachmentParams)
   613  	for _, params := range params {
   614  		sourceName := string(params.Provider)
   615  		paramsBySource[sourceName] = append(paramsBySource[sourceName], params)
   616  		if _, ok := volumeSources[sourceName]; ok {
   617  			continue
   618  		}
   619  		volumeSource, err := volumeSource(
   620  			environConfig, baseStorageDir, sourceName, params.Provider,
   621  		)
   622  		if err != nil {
   623  			return nil, nil, errors.Annotate(err, "getting volume source")
   624  		}
   625  		volumeSources[sourceName] = volumeSource
   626  	}
   627  	return paramsBySource, volumeSources, nil
   628  }
   629  
   630  func volumesFromStorage(in []storage.Volume) []params.Volume {
   631  	out := make([]params.Volume, len(in))
   632  	for i, v := range in {
   633  		out[i] = params.Volume{
   634  			v.Tag.String(),
   635  			params.VolumeInfo{
   636  				v.VolumeId,
   637  				v.HardwareId,
   638  				v.Size,
   639  				v.Persistent,
   640  			},
   641  		}
   642  	}
   643  	return out
   644  }
   645  
   646  func volumeAttachmentsFromStorage(in []storage.VolumeAttachment) []params.VolumeAttachment {
   647  	out := make([]params.VolumeAttachment, len(in))
   648  	for i, v := range in {
   649  		out[i] = params.VolumeAttachment{
   650  			v.Volume.String(),
   651  			v.Machine.String(),
   652  			params.VolumeAttachmentInfo{
   653  				v.DeviceName,
   654  				v.ReadOnly,
   655  			},
   656  		}
   657  	}
   658  	return out
   659  }
   660  
   661  func volumeFromParams(in params.Volume) (storage.Volume, error) {
   662  	volumeTag, err := names.ParseVolumeTag(in.VolumeTag)
   663  	if err != nil {
   664  		return storage.Volume{}, errors.Trace(err)
   665  	}
   666  	return storage.Volume{
   667  		volumeTag,
   668  		storage.VolumeInfo{
   669  			in.Info.VolumeId,
   670  			in.Info.HardwareId,
   671  			in.Info.Size,
   672  			in.Info.Persistent,
   673  		},
   674  	}, nil
   675  }
   676  
   677  func volumeAttachmentFromParams(in params.VolumeAttachment) (storage.VolumeAttachment, error) {
   678  	volumeTag, err := names.ParseVolumeTag(in.VolumeTag)
   679  	if err != nil {
   680  		return storage.VolumeAttachment{}, errors.Trace(err)
   681  	}
   682  	machineTag, err := names.ParseMachineTag(in.MachineTag)
   683  	if err != nil {
   684  		return storage.VolumeAttachment{}, errors.Trace(err)
   685  	}
   686  	return storage.VolumeAttachment{
   687  		volumeTag,
   688  		machineTag,
   689  		storage.VolumeAttachmentInfo{
   690  			in.Info.DeviceName,
   691  			in.Info.ReadOnly,
   692  		},
   693  	}, nil
   694  }
   695  
   696  func volumeParamsFromParams(in params.VolumeParams) (storage.VolumeParams, error) {
   697  	volumeTag, err := names.ParseVolumeTag(in.VolumeTag)
   698  	if err != nil {
   699  		return storage.VolumeParams{}, errors.Trace(err)
   700  	}
   701  	providerType := storage.ProviderType(in.Provider)
   702  
   703  	var attachment *storage.VolumeAttachmentParams
   704  	if in.Attachment != nil {
   705  		if in.Attachment.Provider != in.Provider {
   706  			return storage.VolumeParams{}, errors.Errorf(
   707  				"storage provider mismatch: volume (%q), attachment (%q)",
   708  				in.Provider, in.Attachment.Provider,
   709  			)
   710  		}
   711  		if in.Attachment.VolumeTag != in.VolumeTag {
   712  			return storage.VolumeParams{}, errors.Errorf(
   713  				"volume tag mismatch: volume (%q), attachment (%q)",
   714  				in.VolumeTag, in.Attachment.VolumeTag,
   715  			)
   716  		}
   717  		machineTag, err := names.ParseMachineTag(in.Attachment.MachineTag)
   718  		if err != nil {
   719  			return storage.VolumeParams{}, errors.Annotate(
   720  				err, "parsing attachment machine tag",
   721  			)
   722  		}
   723  		attachment = &storage.VolumeAttachmentParams{
   724  			AttachmentParams: storage.AttachmentParams{
   725  				Provider:   providerType,
   726  				Machine:    machineTag,
   727  				InstanceId: instance.Id(in.Attachment.InstanceId),
   728  				ReadOnly:   in.Attachment.ReadOnly,
   729  			},
   730  			Volume: volumeTag,
   731  		}
   732  	}
   733  	return storage.VolumeParams{
   734  		volumeTag,
   735  		in.Size,
   736  		providerType,
   737  		in.Attributes,
   738  		in.Tags,
   739  		attachment,
   740  	}, nil
   741  }
   742  
   743  func volumeAttachmentParamsFromParams(in params.VolumeAttachmentParams) (storage.VolumeAttachmentParams, error) {
   744  	machineTag, err := names.ParseMachineTag(in.MachineTag)
   745  	if err != nil {
   746  		return storage.VolumeAttachmentParams{}, errors.Trace(err)
   747  	}
   748  	volumeTag, err := names.ParseVolumeTag(in.VolumeTag)
   749  	if err != nil {
   750  		return storage.VolumeAttachmentParams{}, errors.Trace(err)
   751  	}
   752  	return storage.VolumeAttachmentParams{
   753  		AttachmentParams: storage.AttachmentParams{
   754  			Provider:   storage.ProviderType(in.Provider),
   755  			Machine:    machineTag,
   756  			InstanceId: instance.Id(in.InstanceId),
   757  			ReadOnly:   in.ReadOnly,
   758  		},
   759  		Volume:   volumeTag,
   760  		VolumeId: in.VolumeId,
   761  	}, nil
   762  }