github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/storageprovisioner/storageprovisioner.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/loggo"
     9  	"github.com/juju/names"
    10  
    11  	"github.com/juju/juju/apiserver/common"
    12  	"github.com/juju/juju/apiserver/common/storagecommon"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/state"
    15  	"github.com/juju/juju/state/watcher"
    16  	"github.com/juju/juju/storage"
    17  	"github.com/juju/juju/storage/poolmanager"
    18  )
    19  
    20  var logger = loggo.GetLogger("juju.apiserver.storageprovisioner")
    21  
    22  func init() {
    23  	common.RegisterStandardFacade("StorageProvisioner", 2, NewStorageProvisionerAPI)
    24  }
    25  
    26  // StorageProvisionerAPI provides access to the Provisioner API facade.
    27  type StorageProvisionerAPI struct {
    28  	*common.LifeGetter
    29  	*common.DeadEnsurer
    30  	*common.ModelWatcher
    31  	*common.InstanceIdGetter
    32  	*common.StatusSetter
    33  
    34  	st                       provisionerState
    35  	settings                 poolmanager.SettingsManager
    36  	resources                *common.Resources
    37  	authorizer               common.Authorizer
    38  	getScopeAuthFunc         common.GetAuthFunc
    39  	getStorageEntityAuthFunc common.GetAuthFunc
    40  	getMachineAuthFunc       common.GetAuthFunc
    41  	getBlockDevicesAuthFunc  common.GetAuthFunc
    42  	getAttachmentAuthFunc    func() (func(names.MachineTag, names.Tag) bool, error)
    43  }
    44  
    45  var getState = func(st *state.State) provisionerState {
    46  	return stateShim{st}
    47  }
    48  
    49  var getSettingsManager = func(st *state.State) poolmanager.SettingsManager {
    50  	return state.NewStateSettings(st)
    51  }
    52  
    53  // NewStorageProvisionerAPI creates a new server-side StorageProvisionerAPI facade.
    54  func NewStorageProvisionerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*StorageProvisionerAPI, error) {
    55  	if !authorizer.AuthMachineAgent() {
    56  		return nil, common.ErrPerm
    57  	}
    58  	canAccessStorageMachine := func(tag names.MachineTag, allowEnvironManager bool) bool {
    59  		authEntityTag := authorizer.GetAuthTag()
    60  		if tag == authEntityTag {
    61  			// Machine agents can access volumes
    62  			// scoped to their own machine.
    63  			return true
    64  		}
    65  		parentId := state.ParentId(tag.Id())
    66  		if parentId == "" {
    67  			return allowEnvironManager && authorizer.AuthModelManager()
    68  		}
    69  		// All containers with the authenticated
    70  		// machine as a parent are accessible by it.
    71  		return names.NewMachineTag(parentId) == authEntityTag
    72  	}
    73  	getScopeAuthFunc := func() (common.AuthFunc, error) {
    74  		return func(tag names.Tag) bool {
    75  			switch tag := tag.(type) {
    76  			case names.ModelTag:
    77  				// Environment managers can access all volumes
    78  				// and filesystems scoped to the environment.
    79  				isModelManager := authorizer.AuthModelManager()
    80  				return isModelManager && tag == st.ModelTag()
    81  			case names.MachineTag:
    82  				return canAccessStorageMachine(tag, false)
    83  			default:
    84  				return false
    85  			}
    86  		}, nil
    87  	}
    88  	canAccessStorageEntity := func(tag names.Tag, allowMachines bool) bool {
    89  		switch tag := tag.(type) {
    90  		case names.VolumeTag:
    91  			machineTag, ok := names.VolumeMachine(tag)
    92  			if ok {
    93  				return canAccessStorageMachine(machineTag, false)
    94  			}
    95  			return authorizer.AuthModelManager()
    96  		case names.FilesystemTag:
    97  			machineTag, ok := names.FilesystemMachine(tag)
    98  			if ok {
    99  				return canAccessStorageMachine(machineTag, false)
   100  			}
   101  			return authorizer.AuthModelManager()
   102  		case names.MachineTag:
   103  			return allowMachines && canAccessStorageMachine(tag, true)
   104  		default:
   105  			return false
   106  		}
   107  	}
   108  	getStorageEntityAuthFunc := func() (common.AuthFunc, error) {
   109  		return func(tag names.Tag) bool {
   110  			return canAccessStorageEntity(tag, false)
   111  		}, nil
   112  	}
   113  	getLifeAuthFunc := func() (common.AuthFunc, error) {
   114  		return func(tag names.Tag) bool {
   115  			return canAccessStorageEntity(tag, true)
   116  		}, nil
   117  	}
   118  	getAttachmentAuthFunc := func() (func(names.MachineTag, names.Tag) bool, error) {
   119  		// getAttachmentAuthFunc returns a function that validates
   120  		// access by the authenticated user to an attachment.
   121  		return func(machineTag names.MachineTag, attachmentTag names.Tag) bool {
   122  			// Machine agents can access their own machine, and
   123  			// machines contained. Environment managers can access
   124  			// top-level machines.
   125  			if !canAccessStorageMachine(machineTag, true) {
   126  				return false
   127  			}
   128  			// Environment managers can access model-scoped
   129  			// volumes and volumes scoped to their own machines.
   130  			// Other machine agents can access volumes regardless
   131  			// of their scope.
   132  			if !authorizer.AuthModelManager() {
   133  				return true
   134  			}
   135  			var machineScope names.MachineTag
   136  			var hasMachineScope bool
   137  			switch attachmentTag := attachmentTag.(type) {
   138  			case names.VolumeTag:
   139  				machineScope, hasMachineScope = names.VolumeMachine(attachmentTag)
   140  			case names.FilesystemTag:
   141  				machineScope, hasMachineScope = names.FilesystemMachine(attachmentTag)
   142  			}
   143  			return !hasMachineScope || machineScope == authorizer.GetAuthTag()
   144  		}, nil
   145  	}
   146  	getMachineAuthFunc := func() (common.AuthFunc, error) {
   147  		return func(tag names.Tag) bool {
   148  			if tag, ok := tag.(names.MachineTag); ok {
   149  				return canAccessStorageMachine(tag, true)
   150  			}
   151  			return false
   152  		}, nil
   153  	}
   154  	getBlockDevicesAuthFunc := func() (common.AuthFunc, error) {
   155  		return func(tag names.Tag) bool {
   156  			if tag, ok := tag.(names.MachineTag); ok {
   157  				return canAccessStorageMachine(tag, false)
   158  			}
   159  			return false
   160  		}, nil
   161  	}
   162  	stateInterface := getState(st)
   163  	settings := getSettingsManager(st)
   164  	return &StorageProvisionerAPI{
   165  		LifeGetter:       common.NewLifeGetter(stateInterface, getLifeAuthFunc),
   166  		DeadEnsurer:      common.NewDeadEnsurer(stateInterface, getStorageEntityAuthFunc),
   167  		ModelWatcher:     common.NewModelWatcher(stateInterface, resources, authorizer),
   168  		InstanceIdGetter: common.NewInstanceIdGetter(st, getMachineAuthFunc),
   169  		StatusSetter:     common.NewStatusSetter(st, getStorageEntityAuthFunc),
   170  
   171  		st:                       stateInterface,
   172  		settings:                 settings,
   173  		resources:                resources,
   174  		authorizer:               authorizer,
   175  		getScopeAuthFunc:         getScopeAuthFunc,
   176  		getStorageEntityAuthFunc: getStorageEntityAuthFunc,
   177  		getAttachmentAuthFunc:    getAttachmentAuthFunc,
   178  		getMachineAuthFunc:       getMachineAuthFunc,
   179  		getBlockDevicesAuthFunc:  getBlockDevicesAuthFunc,
   180  	}, nil
   181  }
   182  
   183  // WatchBlockDevices watches for changes to the specified machines' block devices.
   184  func (s *StorageProvisionerAPI) WatchBlockDevices(args params.Entities) (params.NotifyWatchResults, error) {
   185  	canAccess, err := s.getBlockDevicesAuthFunc()
   186  	if err != nil {
   187  		return params.NotifyWatchResults{}, common.ServerError(common.ErrPerm)
   188  	}
   189  	results := params.NotifyWatchResults{
   190  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
   191  	}
   192  	one := func(arg params.Entity) (string, error) {
   193  		machineTag, err := names.ParseMachineTag(arg.Tag)
   194  		if err != nil {
   195  			return "", err
   196  		}
   197  		if !canAccess(machineTag) {
   198  			return "", common.ErrPerm
   199  		}
   200  		w := s.st.WatchBlockDevices(machineTag)
   201  		if _, ok := <-w.Changes(); ok {
   202  			return s.resources.Register(w), nil
   203  		}
   204  		return "", watcher.EnsureErr(w)
   205  	}
   206  	for i, arg := range args.Entities {
   207  		var result params.NotifyWatchResult
   208  		id, err := one(arg)
   209  		if err != nil {
   210  			result.Error = common.ServerError(err)
   211  		} else {
   212  			result.NotifyWatcherId = id
   213  		}
   214  		results.Results[i] = result
   215  	}
   216  	return results, nil
   217  }
   218  
   219  // WatchMachines watches for changes to the specified machines.
   220  func (s *StorageProvisionerAPI) WatchMachines(args params.Entities) (params.NotifyWatchResults, error) {
   221  	canAccess, err := s.getMachineAuthFunc()
   222  	if err != nil {
   223  		return params.NotifyWatchResults{}, common.ServerError(common.ErrPerm)
   224  	}
   225  	results := params.NotifyWatchResults{
   226  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
   227  	}
   228  	one := func(arg params.Entity) (string, error) {
   229  		machineTag, err := names.ParseMachineTag(arg.Tag)
   230  		if err != nil {
   231  			return "", err
   232  		}
   233  		if !canAccess(machineTag) {
   234  			return "", common.ErrPerm
   235  		}
   236  		w, err := s.st.WatchMachine(machineTag)
   237  		if err != nil {
   238  			return "", errors.Trace(err)
   239  		}
   240  		if _, ok := <-w.Changes(); ok {
   241  			return s.resources.Register(w), nil
   242  		}
   243  		return "", watcher.EnsureErr(w)
   244  	}
   245  	for i, arg := range args.Entities {
   246  		var result params.NotifyWatchResult
   247  		id, err := one(arg)
   248  		if err != nil {
   249  			result.Error = common.ServerError(err)
   250  		} else {
   251  			result.NotifyWatcherId = id
   252  		}
   253  		results.Results[i] = result
   254  	}
   255  	return results, nil
   256  }
   257  
   258  // WatchVolumes watches for changes to volumes scoped to the
   259  // entity with the tag passed to NewState.
   260  func (s *StorageProvisionerAPI) WatchVolumes(args params.Entities) (params.StringsWatchResults, error) {
   261  	return s.watchStorageEntities(args, s.st.WatchModelVolumes, s.st.WatchMachineVolumes)
   262  }
   263  
   264  // WatchFilesystems watches for changes to filesystems scoped
   265  // to the entity with the tag passed to NewState.
   266  func (s *StorageProvisionerAPI) WatchFilesystems(args params.Entities) (params.StringsWatchResults, error) {
   267  	return s.watchStorageEntities(args, s.st.WatchModelFilesystems, s.st.WatchMachineFilesystems)
   268  }
   269  
   270  func (s *StorageProvisionerAPI) watchStorageEntities(
   271  	args params.Entities,
   272  	watchEnvironStorage func() state.StringsWatcher,
   273  	watchMachineStorage func(names.MachineTag) state.StringsWatcher,
   274  ) (params.StringsWatchResults, error) {
   275  	canAccess, err := s.getScopeAuthFunc()
   276  	if err != nil {
   277  		return params.StringsWatchResults{}, common.ServerError(common.ErrPerm)
   278  	}
   279  	results := params.StringsWatchResults{
   280  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   281  	}
   282  	one := func(arg params.Entity) (string, []string, error) {
   283  		tag, err := names.ParseTag(arg.Tag)
   284  		if err != nil || !canAccess(tag) {
   285  			return "", nil, common.ErrPerm
   286  		}
   287  		var w state.StringsWatcher
   288  		if tag, ok := tag.(names.MachineTag); ok {
   289  			w = watchMachineStorage(tag)
   290  		} else {
   291  			w = watchEnvironStorage()
   292  		}
   293  		if changes, ok := <-w.Changes(); ok {
   294  			return s.resources.Register(w), changes, nil
   295  		}
   296  		return "", nil, watcher.EnsureErr(w)
   297  	}
   298  	for i, arg := range args.Entities {
   299  		var result params.StringsWatchResult
   300  		id, changes, err := one(arg)
   301  		if err != nil {
   302  			result.Error = common.ServerError(err)
   303  		} else {
   304  			result.StringsWatcherId = id
   305  			result.Changes = changes
   306  		}
   307  		results.Results[i] = result
   308  	}
   309  	return results, nil
   310  }
   311  
   312  // WatchVolumeAttachments watches for changes to volume attachments scoped to
   313  // the entity with the tag passed to NewState.
   314  func (s *StorageProvisionerAPI) WatchVolumeAttachments(args params.Entities) (params.MachineStorageIdsWatchResults, error) {
   315  	return s.watchAttachments(
   316  		args,
   317  		s.st.WatchEnvironVolumeAttachments,
   318  		s.st.WatchMachineVolumeAttachments,
   319  		storagecommon.ParseVolumeAttachmentIds,
   320  	)
   321  }
   322  
   323  // WatchFilesystemAttachments watches for changes to filesystem attachments
   324  // scoped to the entity with the tag passed to NewState.
   325  func (s *StorageProvisionerAPI) WatchFilesystemAttachments(args params.Entities) (params.MachineStorageIdsWatchResults, error) {
   326  	return s.watchAttachments(
   327  		args,
   328  		s.st.WatchEnvironFilesystemAttachments,
   329  		s.st.WatchMachineFilesystemAttachments,
   330  		storagecommon.ParseFilesystemAttachmentIds,
   331  	)
   332  }
   333  
   334  func (s *StorageProvisionerAPI) watchAttachments(
   335  	args params.Entities,
   336  	watchEnvironAttachments func() state.StringsWatcher,
   337  	watchMachineAttachments func(names.MachineTag) state.StringsWatcher,
   338  	parseAttachmentIds func([]string) ([]params.MachineStorageId, error),
   339  ) (params.MachineStorageIdsWatchResults, error) {
   340  	canAccess, err := s.getScopeAuthFunc()
   341  	if err != nil {
   342  		return params.MachineStorageIdsWatchResults{}, common.ServerError(common.ErrPerm)
   343  	}
   344  	results := params.MachineStorageIdsWatchResults{
   345  		Results: make([]params.MachineStorageIdsWatchResult, len(args.Entities)),
   346  	}
   347  	one := func(arg params.Entity) (string, []params.MachineStorageId, error) {
   348  		tag, err := names.ParseTag(arg.Tag)
   349  		if err != nil || !canAccess(tag) {
   350  			return "", nil, common.ErrPerm
   351  		}
   352  		var w state.StringsWatcher
   353  		if tag, ok := tag.(names.MachineTag); ok {
   354  			w = watchMachineAttachments(tag)
   355  		} else {
   356  			w = watchEnvironAttachments()
   357  		}
   358  		if stringChanges, ok := <-w.Changes(); ok {
   359  			changes, err := parseAttachmentIds(stringChanges)
   360  			if err != nil {
   361  				w.Stop()
   362  				return "", nil, err
   363  			}
   364  			return s.resources.Register(w), changes, nil
   365  		}
   366  		return "", nil, watcher.EnsureErr(w)
   367  	}
   368  	for i, arg := range args.Entities {
   369  		var result params.MachineStorageIdsWatchResult
   370  		id, changes, err := one(arg)
   371  		if err != nil {
   372  			result.Error = common.ServerError(err)
   373  		} else {
   374  			result.MachineStorageIdsWatcherId = id
   375  			result.Changes = changes
   376  		}
   377  		results.Results[i] = result
   378  	}
   379  	return results, nil
   380  }
   381  
   382  // Volumes returns details of volumes with the specified tags.
   383  func (s *StorageProvisionerAPI) Volumes(args params.Entities) (params.VolumeResults, error) {
   384  	canAccess, err := s.getStorageEntityAuthFunc()
   385  	if err != nil {
   386  		return params.VolumeResults{}, common.ServerError(common.ErrPerm)
   387  	}
   388  	results := params.VolumeResults{
   389  		Results: make([]params.VolumeResult, len(args.Entities)),
   390  	}
   391  	one := func(arg params.Entity) (params.Volume, error) {
   392  		tag, err := names.ParseVolumeTag(arg.Tag)
   393  		if err != nil || !canAccess(tag) {
   394  			return params.Volume{}, common.ErrPerm
   395  		}
   396  		volume, err := s.st.Volume(tag)
   397  		if errors.IsNotFound(err) {
   398  			return params.Volume{}, common.ErrPerm
   399  		} else if err != nil {
   400  			return params.Volume{}, err
   401  		}
   402  		return storagecommon.VolumeFromState(volume)
   403  	}
   404  	for i, arg := range args.Entities {
   405  		var result params.VolumeResult
   406  		volume, err := one(arg)
   407  		if err != nil {
   408  			result.Error = common.ServerError(err)
   409  		} else {
   410  			result.Result = volume
   411  		}
   412  		results.Results[i] = result
   413  	}
   414  	return results, nil
   415  }
   416  
   417  // Filesystems returns details of filesystems with the specified tags.
   418  func (s *StorageProvisionerAPI) Filesystems(args params.Entities) (params.FilesystemResults, error) {
   419  	canAccess, err := s.getStorageEntityAuthFunc()
   420  	if err != nil {
   421  		return params.FilesystemResults{}, common.ServerError(common.ErrPerm)
   422  	}
   423  	results := params.FilesystemResults{
   424  		Results: make([]params.FilesystemResult, len(args.Entities)),
   425  	}
   426  	one := func(arg params.Entity) (params.Filesystem, error) {
   427  		tag, err := names.ParseFilesystemTag(arg.Tag)
   428  		if err != nil || !canAccess(tag) {
   429  			return params.Filesystem{}, common.ErrPerm
   430  		}
   431  		filesystem, err := s.st.Filesystem(tag)
   432  		if errors.IsNotFound(err) {
   433  			return params.Filesystem{}, common.ErrPerm
   434  		} else if err != nil {
   435  			return params.Filesystem{}, err
   436  		}
   437  		return storagecommon.FilesystemFromState(filesystem)
   438  	}
   439  	for i, arg := range args.Entities {
   440  		var result params.FilesystemResult
   441  		filesystem, err := one(arg)
   442  		if err != nil {
   443  			result.Error = common.ServerError(err)
   444  		} else {
   445  			result.Result = filesystem
   446  		}
   447  		results.Results[i] = result
   448  	}
   449  	return results, nil
   450  }
   451  
   452  // VolumeAttachments returns details of volume attachments with the specified IDs.
   453  func (s *StorageProvisionerAPI) VolumeAttachments(args params.MachineStorageIds) (params.VolumeAttachmentResults, error) {
   454  	canAccess, err := s.getAttachmentAuthFunc()
   455  	if err != nil {
   456  		return params.VolumeAttachmentResults{}, common.ServerError(common.ErrPerm)
   457  	}
   458  	results := params.VolumeAttachmentResults{
   459  		Results: make([]params.VolumeAttachmentResult, len(args.Ids)),
   460  	}
   461  	one := func(arg params.MachineStorageId) (params.VolumeAttachment, error) {
   462  		volumeAttachment, err := s.oneVolumeAttachment(arg, canAccess)
   463  		if err != nil {
   464  			return params.VolumeAttachment{}, err
   465  		}
   466  		return storagecommon.VolumeAttachmentFromState(volumeAttachment)
   467  	}
   468  	for i, arg := range args.Ids {
   469  		var result params.VolumeAttachmentResult
   470  		volumeAttachment, err := one(arg)
   471  		if err != nil {
   472  			result.Error = common.ServerError(err)
   473  		} else {
   474  			result.Result = volumeAttachment
   475  		}
   476  		results.Results[i] = result
   477  	}
   478  	return results, nil
   479  }
   480  
   481  // VolumeBlockDevices returns details of the block devices corresponding to the
   482  // volume attachments with the specified IDs.
   483  func (s *StorageProvisionerAPI) VolumeBlockDevices(args params.MachineStorageIds) (params.BlockDeviceResults, error) {
   484  	canAccess, err := s.getAttachmentAuthFunc()
   485  	if err != nil {
   486  		return params.BlockDeviceResults{}, common.ServerError(common.ErrPerm)
   487  	}
   488  	results := params.BlockDeviceResults{
   489  		Results: make([]params.BlockDeviceResult, len(args.Ids)),
   490  	}
   491  	one := func(arg params.MachineStorageId) (storage.BlockDevice, error) {
   492  		stateBlockDevice, err := s.oneVolumeBlockDevice(arg, canAccess)
   493  		if err != nil {
   494  			return storage.BlockDevice{}, err
   495  		}
   496  		return storagecommon.BlockDeviceFromState(stateBlockDevice), nil
   497  	}
   498  	for i, arg := range args.Ids {
   499  		var result params.BlockDeviceResult
   500  		blockDevice, err := one(arg)
   501  		if err != nil {
   502  			result.Error = common.ServerError(err)
   503  		} else {
   504  			result.Result = blockDevice
   505  		}
   506  		results.Results[i] = result
   507  	}
   508  	return results, nil
   509  }
   510  
   511  // FilesystemAttachments returns details of filesystem attachments with the specified IDs.
   512  func (s *StorageProvisionerAPI) FilesystemAttachments(args params.MachineStorageIds) (params.FilesystemAttachmentResults, error) {
   513  	canAccess, err := s.getAttachmentAuthFunc()
   514  	if err != nil {
   515  		return params.FilesystemAttachmentResults{}, common.ServerError(common.ErrPerm)
   516  	}
   517  	results := params.FilesystemAttachmentResults{
   518  		Results: make([]params.FilesystemAttachmentResult, len(args.Ids)),
   519  	}
   520  	one := func(arg params.MachineStorageId) (params.FilesystemAttachment, error) {
   521  		filesystemAttachment, err := s.oneFilesystemAttachment(arg, canAccess)
   522  		if err != nil {
   523  			return params.FilesystemAttachment{}, err
   524  		}
   525  		return storagecommon.FilesystemAttachmentFromState(filesystemAttachment)
   526  	}
   527  	for i, arg := range args.Ids {
   528  		var result params.FilesystemAttachmentResult
   529  		filesystemAttachment, err := one(arg)
   530  		if err != nil {
   531  			result.Error = common.ServerError(err)
   532  		} else {
   533  			result.Result = filesystemAttachment
   534  		}
   535  		results.Results[i] = result
   536  	}
   537  	return results, nil
   538  }
   539  
   540  // VolumeParams returns the parameters for creating or destroying
   541  // the volumes with the specified tags.
   542  func (s *StorageProvisionerAPI) VolumeParams(args params.Entities) (params.VolumeParamsResults, error) {
   543  	canAccess, err := s.getStorageEntityAuthFunc()
   544  	if err != nil {
   545  		return params.VolumeParamsResults{}, err
   546  	}
   547  	envConfig, err := s.st.ModelConfig()
   548  	if err != nil {
   549  		return params.VolumeParamsResults{}, err
   550  	}
   551  	results := params.VolumeParamsResults{
   552  		Results: make([]params.VolumeParamsResult, len(args.Entities)),
   553  	}
   554  	poolManager := poolmanager.New(s.settings)
   555  	one := func(arg params.Entity) (params.VolumeParams, error) {
   556  		tag, err := names.ParseVolumeTag(arg.Tag)
   557  		if err != nil || !canAccess(tag) {
   558  			return params.VolumeParams{}, common.ErrPerm
   559  		}
   560  		volume, err := s.st.Volume(tag)
   561  		if errors.IsNotFound(err) {
   562  			return params.VolumeParams{}, common.ErrPerm
   563  		} else if err != nil {
   564  			return params.VolumeParams{}, err
   565  		}
   566  		volumeAttachments, err := s.st.VolumeAttachments(tag)
   567  		if err != nil {
   568  			return params.VolumeParams{}, err
   569  		}
   570  		storageInstance, err := storagecommon.MaybeAssignedStorageInstance(
   571  			volume.StorageInstance,
   572  			s.st.StorageInstance,
   573  		)
   574  		if err != nil {
   575  			return params.VolumeParams{}, err
   576  		}
   577  		volumeParams, err := storagecommon.VolumeParams(volume, storageInstance, envConfig, poolManager)
   578  		if err != nil {
   579  			return params.VolumeParams{}, err
   580  		}
   581  		if len(volumeAttachments) == 1 {
   582  			// There is exactly one attachment to be made, so make
   583  			// it immediately. Otherwise we will defer attachments
   584  			// until later.
   585  			volumeAttachment := volumeAttachments[0]
   586  			volumeAttachmentParams, ok := volumeAttachment.Params()
   587  			if !ok {
   588  				return params.VolumeParams{}, errors.Errorf(
   589  					"volume %q is already attached to machine %q",
   590  					volumeAttachment.Volume().Id(),
   591  					volumeAttachment.Machine().Id(),
   592  				)
   593  			}
   594  			machineTag := volumeAttachment.Machine()
   595  			instanceId, err := s.st.MachineInstanceId(machineTag)
   596  			if errors.IsNotProvisioned(err) {
   597  				// Leave the attachment until later.
   598  				instanceId = ""
   599  			} else if err != nil {
   600  				return params.VolumeParams{}, err
   601  			}
   602  			volumeParams.Attachment = &params.VolumeAttachmentParams{
   603  				tag.String(),
   604  				machineTag.String(),
   605  				"", // volume ID
   606  				string(instanceId),
   607  				volumeParams.Provider,
   608  				volumeAttachmentParams.ReadOnly,
   609  			}
   610  		}
   611  		return volumeParams, nil
   612  	}
   613  	for i, arg := range args.Entities {
   614  		var result params.VolumeParamsResult
   615  		volumeParams, err := one(arg)
   616  		if err != nil {
   617  			result.Error = common.ServerError(err)
   618  		} else {
   619  			result.Result = volumeParams
   620  		}
   621  		results.Results[i] = result
   622  	}
   623  	return results, nil
   624  }
   625  
   626  // FilesystemParams returns the parameters for creating the filesystems
   627  // with the specified tags.
   628  func (s *StorageProvisionerAPI) FilesystemParams(args params.Entities) (params.FilesystemParamsResults, error) {
   629  	canAccess, err := s.getStorageEntityAuthFunc()
   630  	if err != nil {
   631  		return params.FilesystemParamsResults{}, err
   632  	}
   633  	envConfig, err := s.st.ModelConfig()
   634  	if err != nil {
   635  		return params.FilesystemParamsResults{}, err
   636  	}
   637  	results := params.FilesystemParamsResults{
   638  		Results: make([]params.FilesystemParamsResult, len(args.Entities)),
   639  	}
   640  	poolManager := poolmanager.New(s.settings)
   641  	one := func(arg params.Entity) (params.FilesystemParams, error) {
   642  		tag, err := names.ParseFilesystemTag(arg.Tag)
   643  		if err != nil || !canAccess(tag) {
   644  			return params.FilesystemParams{}, common.ErrPerm
   645  		}
   646  		filesystem, err := s.st.Filesystem(tag)
   647  		if errors.IsNotFound(err) {
   648  			return params.FilesystemParams{}, common.ErrPerm
   649  		} else if err != nil {
   650  			return params.FilesystemParams{}, err
   651  		}
   652  		storageInstance, err := storagecommon.MaybeAssignedStorageInstance(
   653  			filesystem.Storage,
   654  			s.st.StorageInstance,
   655  		)
   656  		if err != nil {
   657  			return params.FilesystemParams{}, err
   658  		}
   659  		filesystemParams, err := storagecommon.FilesystemParams(
   660  			filesystem, storageInstance, envConfig, poolManager,
   661  		)
   662  		if err != nil {
   663  			return params.FilesystemParams{}, err
   664  		}
   665  		return filesystemParams, nil
   666  	}
   667  	for i, arg := range args.Entities {
   668  		var result params.FilesystemParamsResult
   669  		filesystemParams, err := one(arg)
   670  		if err != nil {
   671  			result.Error = common.ServerError(err)
   672  		} else {
   673  			result.Result = filesystemParams
   674  		}
   675  		results.Results[i] = result
   676  	}
   677  	return results, nil
   678  }
   679  
   680  // VolumeAttachmentParams returns the parameters for creating the volume
   681  // attachments with the specified IDs.
   682  func (s *StorageProvisionerAPI) VolumeAttachmentParams(
   683  	args params.MachineStorageIds,
   684  ) (params.VolumeAttachmentParamsResults, error) {
   685  	canAccess, err := s.getAttachmentAuthFunc()
   686  	if err != nil {
   687  		return params.VolumeAttachmentParamsResults{}, common.ServerError(common.ErrPerm)
   688  	}
   689  	results := params.VolumeAttachmentParamsResults{
   690  		Results: make([]params.VolumeAttachmentParamsResult, len(args.Ids)),
   691  	}
   692  	poolManager := poolmanager.New(s.settings)
   693  	one := func(arg params.MachineStorageId) (params.VolumeAttachmentParams, error) {
   694  		volumeAttachment, err := s.oneVolumeAttachment(arg, canAccess)
   695  		if err != nil {
   696  			return params.VolumeAttachmentParams{}, err
   697  		}
   698  		instanceId, err := s.st.MachineInstanceId(volumeAttachment.Machine())
   699  		if errors.IsNotProvisioned(err) {
   700  			// The worker must watch for machine provisioning events.
   701  			instanceId = ""
   702  		} else if err != nil {
   703  			return params.VolumeAttachmentParams{}, err
   704  		}
   705  		volume, err := s.st.Volume(volumeAttachment.Volume())
   706  		if err != nil {
   707  			return params.VolumeAttachmentParams{}, err
   708  		}
   709  		var volumeId string
   710  		var pool string
   711  		if volumeParams, ok := volume.Params(); ok {
   712  			pool = volumeParams.Pool
   713  		} else {
   714  			volumeInfo, err := volume.Info()
   715  			if err != nil {
   716  				return params.VolumeAttachmentParams{}, err
   717  			}
   718  			volumeId = volumeInfo.VolumeId
   719  			pool = volumeInfo.Pool
   720  		}
   721  		providerType, _, err := storagecommon.StoragePoolConfig(pool, poolManager)
   722  		if err != nil {
   723  			return params.VolumeAttachmentParams{}, errors.Trace(err)
   724  		}
   725  		var readOnly bool
   726  		if volumeAttachmentParams, ok := volumeAttachment.Params(); ok {
   727  			readOnly = volumeAttachmentParams.ReadOnly
   728  		} else {
   729  			// Attachment parameters may be requested even if the
   730  			// attachment exists; i.e. for reattachment.
   731  			volumeAttachmentInfo, err := volumeAttachment.Info()
   732  			if err != nil {
   733  				return params.VolumeAttachmentParams{}, errors.Trace(err)
   734  			}
   735  			readOnly = volumeAttachmentInfo.ReadOnly
   736  		}
   737  		return params.VolumeAttachmentParams{
   738  			volumeAttachment.Volume().String(),
   739  			volumeAttachment.Machine().String(),
   740  			volumeId,
   741  			string(instanceId),
   742  			string(providerType),
   743  			readOnly,
   744  		}, nil
   745  	}
   746  	for i, arg := range args.Ids {
   747  		var result params.VolumeAttachmentParamsResult
   748  		volumeAttachment, err := one(arg)
   749  		if err != nil {
   750  			result.Error = common.ServerError(err)
   751  		} else {
   752  			result.Result = volumeAttachment
   753  		}
   754  		results.Results[i] = result
   755  	}
   756  	return results, nil
   757  }
   758  
   759  // FilesystemAttachmentParams returns the parameters for creating the filesystem
   760  // attachments with the specified IDs.
   761  func (s *StorageProvisionerAPI) FilesystemAttachmentParams(
   762  	args params.MachineStorageIds,
   763  ) (params.FilesystemAttachmentParamsResults, error) {
   764  	canAccess, err := s.getAttachmentAuthFunc()
   765  	if err != nil {
   766  		return params.FilesystemAttachmentParamsResults{}, common.ServerError(common.ErrPerm)
   767  	}
   768  	results := params.FilesystemAttachmentParamsResults{
   769  		Results: make([]params.FilesystemAttachmentParamsResult, len(args.Ids)),
   770  	}
   771  	poolManager := poolmanager.New(s.settings)
   772  	one := func(arg params.MachineStorageId) (params.FilesystemAttachmentParams, error) {
   773  		filesystemAttachment, err := s.oneFilesystemAttachment(arg, canAccess)
   774  		if err != nil {
   775  			return params.FilesystemAttachmentParams{}, err
   776  		}
   777  		instanceId, err := s.st.MachineInstanceId(filesystemAttachment.Machine())
   778  		if errors.IsNotProvisioned(err) {
   779  			// The worker must watch for machine provisioning events.
   780  			instanceId = ""
   781  		} else if err != nil {
   782  			return params.FilesystemAttachmentParams{}, err
   783  		}
   784  		filesystem, err := s.st.Filesystem(filesystemAttachment.Filesystem())
   785  		if err != nil {
   786  			return params.FilesystemAttachmentParams{}, err
   787  		}
   788  		var filesystemId string
   789  		var pool string
   790  		if filesystemParams, ok := filesystem.Params(); ok {
   791  			pool = filesystemParams.Pool
   792  		} else {
   793  			filesystemInfo, err := filesystem.Info()
   794  			if err != nil {
   795  				return params.FilesystemAttachmentParams{}, err
   796  			}
   797  			filesystemId = filesystemInfo.FilesystemId
   798  			pool = filesystemInfo.Pool
   799  		}
   800  		providerType, _, err := storagecommon.StoragePoolConfig(pool, poolManager)
   801  		if err != nil {
   802  			return params.FilesystemAttachmentParams{}, errors.Trace(err)
   803  		}
   804  		var location string
   805  		var readOnly bool
   806  		if filesystemAttachmentParams, ok := filesystemAttachment.Params(); ok {
   807  			location = filesystemAttachmentParams.Location
   808  			readOnly = filesystemAttachmentParams.ReadOnly
   809  		} else {
   810  			// Attachment parameters may be requested even if the
   811  			// attachment exists; i.e. for reattachment.
   812  			filesystemAttachmentInfo, err := filesystemAttachment.Info()
   813  			if err != nil {
   814  				return params.FilesystemAttachmentParams{}, errors.Trace(err)
   815  			}
   816  			location = filesystemAttachmentInfo.MountPoint
   817  			readOnly = filesystemAttachmentInfo.ReadOnly
   818  		}
   819  		return params.FilesystemAttachmentParams{
   820  			filesystemAttachment.Filesystem().String(),
   821  			filesystemAttachment.Machine().String(),
   822  			filesystemId,
   823  			string(instanceId),
   824  			string(providerType),
   825  			// TODO(axw) dealias MountPoint. We now have
   826  			// Path, MountPoint and Location in different
   827  			// parts of the codebase.
   828  			location,
   829  			readOnly,
   830  		}, nil
   831  	}
   832  	for i, arg := range args.Ids {
   833  		var result params.FilesystemAttachmentParamsResult
   834  		filesystemAttachment, err := one(arg)
   835  		if err != nil {
   836  			result.Error = common.ServerError(err)
   837  		} else {
   838  			result.Result = filesystemAttachment
   839  		}
   840  		results.Results[i] = result
   841  	}
   842  	return results, nil
   843  }
   844  
   845  func (s *StorageProvisionerAPI) oneVolumeAttachment(
   846  	id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool,
   847  ) (state.VolumeAttachment, error) {
   848  	machineTag, err := names.ParseMachineTag(id.MachineTag)
   849  	if err != nil {
   850  		return nil, err
   851  	}
   852  	volumeTag, err := names.ParseVolumeTag(id.AttachmentTag)
   853  	if err != nil {
   854  		return nil, err
   855  	}
   856  	if !canAccess(machineTag, volumeTag) {
   857  		return nil, common.ErrPerm
   858  	}
   859  	volumeAttachment, err := s.st.VolumeAttachment(machineTag, volumeTag)
   860  	if errors.IsNotFound(err) {
   861  		return nil, common.ErrPerm
   862  	} else if err != nil {
   863  		return nil, err
   864  	}
   865  	return volumeAttachment, nil
   866  }
   867  
   868  func (s *StorageProvisionerAPI) oneVolumeBlockDevice(
   869  	id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool,
   870  ) (state.BlockDeviceInfo, error) {
   871  	volumeAttachment, err := s.oneVolumeAttachment(id, canAccess)
   872  	if err != nil {
   873  		return state.BlockDeviceInfo{}, err
   874  	}
   875  	volume, err := s.st.Volume(volumeAttachment.Volume())
   876  	if err != nil {
   877  		return state.BlockDeviceInfo{}, err
   878  	}
   879  	volumeInfo, err := volume.Info()
   880  	if err != nil {
   881  		return state.BlockDeviceInfo{}, err
   882  	}
   883  	volumeAttachmentInfo, err := volumeAttachment.Info()
   884  	if err != nil {
   885  		return state.BlockDeviceInfo{}, err
   886  	}
   887  	blockDevices, err := s.st.BlockDevices(volumeAttachment.Machine())
   888  	if err != nil {
   889  		return state.BlockDeviceInfo{}, err
   890  	}
   891  	blockDevice, ok := storagecommon.MatchingBlockDevice(
   892  		blockDevices,
   893  		volumeInfo,
   894  		volumeAttachmentInfo,
   895  	)
   896  	if !ok {
   897  		return state.BlockDeviceInfo{}, errors.NotFoundf(
   898  			"block device for volume %v on machine %v",
   899  			volumeAttachment.Volume().Id(),
   900  			volumeAttachment.Machine().Id(),
   901  		)
   902  	}
   903  	return *blockDevice, nil
   904  }
   905  
   906  func (s *StorageProvisionerAPI) oneFilesystemAttachment(
   907  	id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool,
   908  ) (state.FilesystemAttachment, error) {
   909  	machineTag, err := names.ParseMachineTag(id.MachineTag)
   910  	if err != nil {
   911  		return nil, err
   912  	}
   913  	filesystemTag, err := names.ParseFilesystemTag(id.AttachmentTag)
   914  	if err != nil {
   915  		return nil, err
   916  	}
   917  	if !canAccess(machineTag, filesystemTag) {
   918  		return nil, common.ErrPerm
   919  	}
   920  	filesystemAttachment, err := s.st.FilesystemAttachment(machineTag, filesystemTag)
   921  	if errors.IsNotFound(err) {
   922  		return nil, common.ErrPerm
   923  	} else if err != nil {
   924  		return nil, err
   925  	}
   926  	return filesystemAttachment, nil
   927  }
   928  
   929  // SetVolumeInfo records the details of newly provisioned volumes.
   930  func (s *StorageProvisionerAPI) SetVolumeInfo(args params.Volumes) (params.ErrorResults, error) {
   931  	canAccessVolume, err := s.getStorageEntityAuthFunc()
   932  	if err != nil {
   933  		return params.ErrorResults{}, err
   934  	}
   935  	results := params.ErrorResults{
   936  		Results: make([]params.ErrorResult, len(args.Volumes)),
   937  	}
   938  	one := func(arg params.Volume) error {
   939  		volumeTag, volumeInfo, err := storagecommon.VolumeToState(arg)
   940  		if err != nil {
   941  			return errors.Trace(err)
   942  		} else if !canAccessVolume(volumeTag) {
   943  			return common.ErrPerm
   944  		}
   945  		err = s.st.SetVolumeInfo(volumeTag, volumeInfo)
   946  		if errors.IsNotFound(err) {
   947  			return common.ErrPerm
   948  		}
   949  		return errors.Trace(err)
   950  	}
   951  	for i, arg := range args.Volumes {
   952  		err := one(arg)
   953  		results.Results[i].Error = common.ServerError(err)
   954  	}
   955  	return results, nil
   956  }
   957  
   958  // SetFilesystemInfo records the details of newly provisioned filesystems.
   959  func (s *StorageProvisionerAPI) SetFilesystemInfo(args params.Filesystems) (params.ErrorResults, error) {
   960  	canAccessFilesystem, err := s.getStorageEntityAuthFunc()
   961  	if err != nil {
   962  		return params.ErrorResults{}, err
   963  	}
   964  	results := params.ErrorResults{
   965  		Results: make([]params.ErrorResult, len(args.Filesystems)),
   966  	}
   967  	one := func(arg params.Filesystem) error {
   968  		filesystemTag, filesystemInfo, err := storagecommon.FilesystemToState(arg)
   969  		if err != nil {
   970  			return errors.Trace(err)
   971  		} else if !canAccessFilesystem(filesystemTag) {
   972  			return common.ErrPerm
   973  		}
   974  		err = s.st.SetFilesystemInfo(filesystemTag, filesystemInfo)
   975  		if errors.IsNotFound(err) {
   976  			return common.ErrPerm
   977  		}
   978  		return errors.Trace(err)
   979  	}
   980  	for i, arg := range args.Filesystems {
   981  		err := one(arg)
   982  		results.Results[i].Error = common.ServerError(err)
   983  	}
   984  	return results, nil
   985  }
   986  
   987  // SetVolumeAttachmentInfo records the details of newly provisioned volume
   988  // attachments.
   989  func (s *StorageProvisionerAPI) SetVolumeAttachmentInfo(
   990  	args params.VolumeAttachments,
   991  ) (params.ErrorResults, error) {
   992  	canAccess, err := s.getAttachmentAuthFunc()
   993  	if err != nil {
   994  		return params.ErrorResults{}, err
   995  	}
   996  	results := params.ErrorResults{
   997  		Results: make([]params.ErrorResult, len(args.VolumeAttachments)),
   998  	}
   999  	one := func(arg params.VolumeAttachment) error {
  1000  		machineTag, volumeTag, volumeAttachmentInfo, err := storagecommon.VolumeAttachmentToState(arg)
  1001  		if err != nil {
  1002  			return errors.Trace(err)
  1003  		}
  1004  		if !canAccess(machineTag, volumeTag) {
  1005  			return common.ErrPerm
  1006  		}
  1007  		err = s.st.SetVolumeAttachmentInfo(machineTag, volumeTag, volumeAttachmentInfo)
  1008  		if errors.IsNotFound(err) {
  1009  			return common.ErrPerm
  1010  		}
  1011  		return errors.Trace(err)
  1012  	}
  1013  	for i, arg := range args.VolumeAttachments {
  1014  		err := one(arg)
  1015  		results.Results[i].Error = common.ServerError(err)
  1016  	}
  1017  	return results, nil
  1018  }
  1019  
  1020  // SetFilesystemAttachmentInfo records the details of newly provisioned filesystem
  1021  // attachments.
  1022  func (s *StorageProvisionerAPI) SetFilesystemAttachmentInfo(
  1023  	args params.FilesystemAttachments,
  1024  ) (params.ErrorResults, error) {
  1025  	canAccess, err := s.getAttachmentAuthFunc()
  1026  	if err != nil {
  1027  		return params.ErrorResults{}, err
  1028  	}
  1029  	results := params.ErrorResults{
  1030  		Results: make([]params.ErrorResult, len(args.FilesystemAttachments)),
  1031  	}
  1032  	one := func(arg params.FilesystemAttachment) error {
  1033  		machineTag, filesystemTag, filesystemAttachmentInfo, err := storagecommon.FilesystemAttachmentToState(arg)
  1034  		if err != nil {
  1035  			return errors.Trace(err)
  1036  		}
  1037  		if !canAccess(machineTag, filesystemTag) {
  1038  			return common.ErrPerm
  1039  		}
  1040  		err = s.st.SetFilesystemAttachmentInfo(machineTag, filesystemTag, filesystemAttachmentInfo)
  1041  		if errors.IsNotFound(err) {
  1042  			return common.ErrPerm
  1043  		}
  1044  		return errors.Trace(err)
  1045  	}
  1046  	for i, arg := range args.FilesystemAttachments {
  1047  		err := one(arg)
  1048  		results.Results[i].Error = common.ServerError(err)
  1049  	}
  1050  	return results, nil
  1051  }
  1052  
  1053  // AttachmentLife returns the lifecycle state of each specified machine
  1054  // storage attachment.
  1055  func (s *StorageProvisionerAPI) AttachmentLife(args params.MachineStorageIds) (params.LifeResults, error) {
  1056  	canAccess, err := s.getAttachmentAuthFunc()
  1057  	if err != nil {
  1058  		return params.LifeResults{}, err
  1059  	}
  1060  	results := params.LifeResults{
  1061  		Results: make([]params.LifeResult, len(args.Ids)),
  1062  	}
  1063  	one := func(arg params.MachineStorageId) (params.Life, error) {
  1064  		machineTag, err := names.ParseMachineTag(arg.MachineTag)
  1065  		if err != nil {
  1066  			return "", err
  1067  		}
  1068  		attachmentTag, err := names.ParseTag(arg.AttachmentTag)
  1069  		if err != nil {
  1070  			return "", err
  1071  		}
  1072  		if !canAccess(machineTag, attachmentTag) {
  1073  			return "", common.ErrPerm
  1074  		}
  1075  		var lifer state.Lifer
  1076  		switch attachmentTag := attachmentTag.(type) {
  1077  		case names.VolumeTag:
  1078  			lifer, err = s.st.VolumeAttachment(machineTag, attachmentTag)
  1079  		case names.FilesystemTag:
  1080  			lifer, err = s.st.FilesystemAttachment(machineTag, attachmentTag)
  1081  		}
  1082  		if errors.IsNotFound(err) {
  1083  			return "", common.ErrPerm
  1084  		} else if err != nil {
  1085  			return "", errors.Trace(err)
  1086  		}
  1087  		return params.Life(lifer.Life().String()), nil
  1088  	}
  1089  	for i, arg := range args.Ids {
  1090  		life, err := one(arg)
  1091  		if err != nil {
  1092  			results.Results[i].Error = common.ServerError(err)
  1093  		} else {
  1094  			results.Results[i].Life = life
  1095  		}
  1096  	}
  1097  	return results, nil
  1098  }
  1099  
  1100  // Remove removes volumes and filesystems from state.
  1101  func (s *StorageProvisionerAPI) Remove(args params.Entities) (params.ErrorResults, error) {
  1102  	canAccess, err := s.getStorageEntityAuthFunc()
  1103  	if err != nil {
  1104  		return params.ErrorResults{}, err
  1105  	}
  1106  	results := params.ErrorResults{
  1107  		Results: make([]params.ErrorResult, len(args.Entities)),
  1108  	}
  1109  	one := func(arg params.Entity) error {
  1110  		tag, err := names.ParseTag(arg.Tag)
  1111  		if err != nil {
  1112  			return errors.Trace(err)
  1113  		}
  1114  		if !canAccess(tag) {
  1115  			return common.ErrPerm
  1116  		}
  1117  		switch tag := tag.(type) {
  1118  		case names.FilesystemTag:
  1119  			return s.st.RemoveFilesystem(tag)
  1120  		case names.VolumeTag:
  1121  			return s.st.RemoveVolume(tag)
  1122  		default:
  1123  			// should have been picked up by canAccess
  1124  			logger.Debugf("unexpected %v tag", tag.Kind())
  1125  			return common.ErrPerm
  1126  		}
  1127  	}
  1128  	for i, arg := range args.Entities {
  1129  		err := one(arg)
  1130  		results.Results[i].Error = common.ServerError(err)
  1131  	}
  1132  	return results, nil
  1133  }
  1134  
  1135  // RemoveAttachments removes the specified machine storage attachments
  1136  // from state.
  1137  func (s *StorageProvisionerAPI) RemoveAttachment(args params.MachineStorageIds) (params.ErrorResults, error) {
  1138  	canAccess, err := s.getAttachmentAuthFunc()
  1139  	if err != nil {
  1140  		return params.ErrorResults{}, err
  1141  	}
  1142  	results := params.ErrorResults{
  1143  		Results: make([]params.ErrorResult, len(args.Ids)),
  1144  	}
  1145  	removeAttachment := func(arg params.MachineStorageId) error {
  1146  		machineTag, err := names.ParseMachineTag(arg.MachineTag)
  1147  		if err != nil {
  1148  			return err
  1149  		}
  1150  		attachmentTag, err := names.ParseTag(arg.AttachmentTag)
  1151  		if err != nil {
  1152  			return err
  1153  		}
  1154  		if !canAccess(machineTag, attachmentTag) {
  1155  			return common.ErrPerm
  1156  		}
  1157  		switch attachmentTag := attachmentTag.(type) {
  1158  		case names.VolumeTag:
  1159  			return s.st.RemoveVolumeAttachment(machineTag, attachmentTag)
  1160  		case names.FilesystemTag:
  1161  			return s.st.RemoveFilesystemAttachment(machineTag, attachmentTag)
  1162  		default:
  1163  			return common.ErrPerm
  1164  		}
  1165  	}
  1166  	for i, arg := range args.Ids {
  1167  		if err := removeAttachment(arg); err != nil {
  1168  			results.Results[i].Error = common.ServerError(err)
  1169  		}
  1170  	}
  1171  	return results, nil
  1172  }