github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/storage/storage.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storage
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/collections/set"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  
    13  	"github.com/juju/juju/apiserver/authentication"
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/common/storagecommon"
    16  	apiservererrors "github.com/juju/juju/apiserver/errors"
    17  	"github.com/juju/juju/apiserver/facade"
    18  	k8sconstants "github.com/juju/juju/caas/kubernetes/provider/constants"
    19  	"github.com/juju/juju/core/permission"
    20  	"github.com/juju/juju/environs/context"
    21  	"github.com/juju/juju/environs/tags"
    22  	"github.com/juju/juju/rpc/params"
    23  	"github.com/juju/juju/state"
    24  	"github.com/juju/juju/storage"
    25  	"github.com/juju/juju/storage/poolmanager"
    26  )
    27  
    28  type storageMetadataFunc func() (poolmanager.PoolManager, storage.ProviderRegistry, error)
    29  
    30  // StorageAPI implements the latest version (v6) of the Storage API.
    31  type StorageAPI struct {
    32  	backend         backend
    33  	storageAccess   storageAccess
    34  	storageMetadata storageMetadataFunc
    35  	authorizer      facade.Authorizer
    36  	callContext     context.ProviderCallContext
    37  	modelType       state.ModelType
    38  }
    39  
    40  func NewStorageAPI(
    41  	backend backend,
    42  	modelType state.ModelType,
    43  	storageAccess storageAccess,
    44  	storageMetadata storageMetadataFunc,
    45  	authorizer facade.Authorizer,
    46  	callContext context.ProviderCallContext,
    47  ) *StorageAPI {
    48  	return &StorageAPI{
    49  		backend:         backend,
    50  		modelType:       modelType,
    51  		storageAccess:   storageAccess,
    52  		storageMetadata: storageMetadata,
    53  		authorizer:      authorizer,
    54  		callContext:     callContext,
    55  	}
    56  }
    57  
    58  func (a *StorageAPI) checkCanRead() error {
    59  	err := a.authorizer.HasPermission(permission.SuperuserAccess, a.backend.ControllerTag())
    60  	if err != nil && !errors.Is(err, authentication.ErrorEntityMissingPermission) {
    61  		return errors.Trace(err)
    62  	}
    63  
    64  	if err == nil {
    65  		return nil
    66  	}
    67  	return a.authorizer.HasPermission(permission.ReadAccess, a.backend.ModelTag())
    68  }
    69  
    70  func (a *StorageAPI) checkCanWrite() error {
    71  	return a.authorizer.HasPermission(permission.WriteAccess, a.backend.ModelTag())
    72  }
    73  
    74  // StorageDetails retrieves and returns detailed information about desired
    75  // storage identified by supplied tags. If specified storage cannot be
    76  // retrieved, individual error is returned instead of storage information.
    77  func (a *StorageAPI) StorageDetails(entities params.Entities) (params.StorageDetailsResults, error) {
    78  	if err := a.checkCanRead(); err != nil {
    79  		return params.StorageDetailsResults{}, errors.Trace(err)
    80  	}
    81  	results := make([]params.StorageDetailsResult, len(entities.Entities))
    82  	for i, entity := range entities.Entities {
    83  		storageTag, err := names.ParseStorageTag(entity.Tag)
    84  		if err != nil {
    85  			results[i].Error = apiservererrors.ServerError(err)
    86  			continue
    87  		}
    88  		storageInstance, err := a.storageAccess.StorageInstance(storageTag)
    89  		if err != nil {
    90  			results[i].Error = apiservererrors.ServerError(err)
    91  			continue
    92  		}
    93  		details, err := storagecommon.StorageDetails(a.storageAccess, a.unitAssignedMachine, storageInstance)
    94  		if err != nil {
    95  			results[i].Error = apiservererrors.ServerError(err)
    96  			continue
    97  		}
    98  		results[i].Result = details
    99  	}
   100  	return params.StorageDetailsResults{Results: results}, nil
   101  }
   102  
   103  // ListStorageDetails returns storage matching a filter.
   104  func (a *StorageAPI) ListStorageDetails(filters params.StorageFilters) (params.StorageDetailsListResults, error) {
   105  	if err := a.checkCanRead(); err != nil {
   106  		return params.StorageDetailsListResults{}, errors.Trace(err)
   107  	}
   108  	results := params.StorageDetailsListResults{
   109  		Results: make([]params.StorageDetailsListResult, len(filters.Filters)),
   110  	}
   111  	for i, filter := range filters.Filters {
   112  		list, err := a.listStorageDetails(filter)
   113  		if err != nil {
   114  			results.Results[i].Error = apiservererrors.ServerError(err)
   115  			continue
   116  		}
   117  		results.Results[i].Result = list
   118  	}
   119  	return results, nil
   120  }
   121  
   122  func (a *StorageAPI) listStorageDetails(filter params.StorageFilter) ([]params.StorageDetails, error) {
   123  	if filter != (params.StorageFilter{}) {
   124  		// StorageFilter has no fields at the time of writing, but
   125  		// check that no fields are set in case we forget to update
   126  		// this code.
   127  		return nil, errors.NotSupportedf("storage filters")
   128  	}
   129  	stateInstances, err := a.storageAccess.AllStorageInstances()
   130  	if err != nil {
   131  		return nil, apiservererrors.ServerError(err)
   132  	}
   133  	results := make([]params.StorageDetails, len(stateInstances))
   134  	for i, stateInstance := range stateInstances {
   135  		details, err := storagecommon.StorageDetails(a.storageAccess, a.unitAssignedMachine, stateInstance)
   136  		if err != nil {
   137  			return nil, errors.Annotatef(
   138  				err, "getting details for %s",
   139  				names.ReadableString(stateInstance.Tag()),
   140  			)
   141  		}
   142  		results[i] = *details
   143  	}
   144  	return results, nil
   145  }
   146  
   147  // ListPools returns a list of pools.
   148  // If filter is provided, returned list only contains pools that match
   149  // the filter.
   150  // Pools can be filtered on names and provider types.
   151  // If both names and types are provided as filter,
   152  // pools that match either are returned.
   153  // This method lists union of pools and environment provider types.
   154  // If no filter is provided, all pools are returned.
   155  func (a *StorageAPI) ListPools(
   156  	filters params.StoragePoolFilters,
   157  ) (params.StoragePoolsResults, error) {
   158  	if err := a.checkCanRead(); err != nil {
   159  		return params.StoragePoolsResults{}, errors.Trace(err)
   160  	}
   161  
   162  	results := params.StoragePoolsResults{
   163  		Results: make([]params.StoragePoolsResult, len(filters.Filters)),
   164  	}
   165  	for i, filter := range filters.Filters {
   166  		pools, err := a.listPools(a.ensureStoragePoolFilter(filter))
   167  		if err != nil {
   168  			results.Results[i].Error = apiservererrors.ServerError(err)
   169  			continue
   170  		}
   171  		results.Results[i].Result = pools
   172  	}
   173  	return results, nil
   174  }
   175  
   176  func (a *StorageAPI) ensureStoragePoolFilter(filter params.StoragePoolFilter) params.StoragePoolFilter {
   177  	if a.modelType == state.ModelTypeCAAS {
   178  		filter.Providers = append(filter.Providers, k8sconstants.CAASProviderType)
   179  	}
   180  	return filter
   181  }
   182  
   183  func (a *StorageAPI) listPools(filter params.StoragePoolFilter) ([]params.StoragePool, error) {
   184  	pm, registry, err := a.storageMetadata()
   185  	if err != nil {
   186  		return nil, errors.Trace(err)
   187  	}
   188  	if err := a.validatePoolListFilter(registry, filter); err != nil {
   189  		return nil, errors.Trace(err)
   190  	}
   191  
   192  	pools, err := pm.List()
   193  	if err != nil {
   194  		return nil, errors.Trace(err)
   195  	}
   196  	providers, err := registry.StorageProviderTypes()
   197  	if err != nil {
   198  		return nil, errors.Trace(err)
   199  	}
   200  	matches := buildFilter(filter)
   201  	results := append(
   202  		filterPools(pools, matches),
   203  		filterProviders(providers, matches)...,
   204  	)
   205  	return results, nil
   206  }
   207  
   208  func buildFilter(filter params.StoragePoolFilter) func(n, p string) bool {
   209  	providerSet := set.NewStrings(filter.Providers...)
   210  	nameSet := set.NewStrings(filter.Names...)
   211  
   212  	matches := func(n, p string) bool {
   213  		// no filters supplied = pool matches criteria
   214  		if providerSet.IsEmpty() && nameSet.IsEmpty() {
   215  			return true
   216  		}
   217  		// if at least 1 name and type are supplied, use AND to match
   218  		if !providerSet.IsEmpty() && !nameSet.IsEmpty() {
   219  			return nameSet.Contains(n) && providerSet.Contains(p)
   220  		}
   221  		// Otherwise, if only names or types are supplied, use OR to match
   222  		return nameSet.Contains(n) || providerSet.Contains(p)
   223  	}
   224  	return matches
   225  }
   226  
   227  func filterProviders(
   228  	providers []storage.ProviderType,
   229  	matches func(n, p string) bool,
   230  ) []params.StoragePool {
   231  	if len(providers) == 0 {
   232  		return nil
   233  	}
   234  	all := make([]params.StoragePool, 0, len(providers))
   235  	for _, p := range providers {
   236  		ps := string(p)
   237  		if matches(ps, ps) {
   238  			all = append(all, params.StoragePool{Name: ps, Provider: ps})
   239  		}
   240  	}
   241  	return all
   242  }
   243  
   244  func filterPools(
   245  	pools []*storage.Config,
   246  	matches func(n, p string) bool,
   247  ) []params.StoragePool {
   248  	if len(pools) == 0 {
   249  		return nil
   250  	}
   251  	all := make([]params.StoragePool, 0, len(pools))
   252  	for _, p := range pools {
   253  		if matches(p.Name(), string(p.Provider())) {
   254  			all = append(all, params.StoragePool{
   255  				Name:     p.Name(),
   256  				Provider: string(p.Provider()),
   257  				Attrs:    p.Attrs(),
   258  			})
   259  		}
   260  	}
   261  	return all
   262  }
   263  
   264  func (a *StorageAPI) validatePoolListFilter(registry storage.ProviderRegistry, filter params.StoragePoolFilter) error {
   265  	if err := a.validateProviderCriteria(registry, filter.Providers); err != nil {
   266  		return errors.Trace(err)
   267  	}
   268  	if err := a.validateNameCriteria(filter.Names); err != nil {
   269  		return errors.Trace(err)
   270  	}
   271  	return nil
   272  }
   273  
   274  func (a *StorageAPI) validateNameCriteria(names []string) error {
   275  	for _, n := range names {
   276  		if !storage.IsValidPoolName(n) {
   277  			return errors.NotValidf("pool name %q", n)
   278  		}
   279  	}
   280  	return nil
   281  }
   282  
   283  func (a *StorageAPI) validateProviderCriteria(registry storage.ProviderRegistry, providers []string) error {
   284  	for _, p := range providers {
   285  		_, err := registry.StorageProvider(storage.ProviderType(p))
   286  		if err != nil {
   287  			return errors.Trace(err)
   288  		}
   289  	}
   290  	return nil
   291  }
   292  
   293  // CreatePool creates a new pool with specified parameters.
   294  func (a *StorageAPI) CreatePool(p params.StoragePoolArgs) (params.ErrorResults, error) {
   295  	results := params.ErrorResults{
   296  		Results: make([]params.ErrorResult, len(p.Pools)),
   297  	}
   298  	pm, _, err := a.storageMetadata()
   299  	if err != nil {
   300  		return params.ErrorResults{}, errors.Trace(err)
   301  	}
   302  	for i, pool := range p.Pools {
   303  		_, err := pm.Create(
   304  			pool.Name,
   305  			storage.ProviderType(pool.Provider),
   306  			pool.Attrs)
   307  		results.Results[i].Error = apiservererrors.ServerError(err)
   308  	}
   309  	return results, nil
   310  }
   311  
   312  // ListVolumes lists volumes with the given filters. Each filter produces
   313  // an independent list of volumes, or an error if the filter is invalid
   314  // or the volumes could not be listed.
   315  func (a *StorageAPI) ListVolumes(filters params.VolumeFilters) (params.VolumeDetailsListResults, error) {
   316  	if err := a.checkCanRead(); err != nil {
   317  		return params.VolumeDetailsListResults{}, errors.Trace(err)
   318  	}
   319  	results := params.VolumeDetailsListResults{
   320  		Results: make([]params.VolumeDetailsListResult, len(filters.Filters)),
   321  	}
   322  	for i, filter := range filters.Filters {
   323  		volumes, volumeAttachments, err := filterVolumes(a.storageAccess, filter)
   324  		if err != nil {
   325  			results.Results[i].Error = apiservererrors.ServerError(err)
   326  			continue
   327  		}
   328  		details, err := a.createVolumeDetailsList(volumes, volumeAttachments)
   329  		if err != nil {
   330  			results.Results[i].Error = apiservererrors.ServerError(err)
   331  			continue
   332  		}
   333  		results.Results[i].Result = details
   334  	}
   335  	return results, nil
   336  }
   337  
   338  func filterVolumes(
   339  	stVolume storageVolume,
   340  	f params.VolumeFilter,
   341  ) ([]state.Volume, map[names.VolumeTag][]state.VolumeAttachment, error) {
   342  	// Exit early if there's no volume support.
   343  	if stVolume == nil {
   344  		return nil, nil, nil
   345  	}
   346  	if f.IsEmpty() {
   347  		// No filter was specified: get all volumes, and all attachments.
   348  		volumes, err := stVolume.AllVolumes()
   349  		if err != nil {
   350  			return nil, nil, errors.Trace(err)
   351  		}
   352  		volumeAttachments := make(map[names.VolumeTag][]state.VolumeAttachment)
   353  		for _, v := range volumes {
   354  			attachments, err := stVolume.VolumeAttachments(v.VolumeTag())
   355  			if err != nil {
   356  				return nil, nil, errors.Trace(err)
   357  			}
   358  			volumeAttachments[v.VolumeTag()] = attachments
   359  		}
   360  		return volumes, volumeAttachments, nil
   361  	}
   362  	volumesByTag := make(map[names.VolumeTag]state.Volume)
   363  	volumeAttachments := make(map[names.VolumeTag][]state.VolumeAttachment)
   364  	for _, machine := range f.Machines {
   365  		machineTag, err := names.ParseMachineTag(machine)
   366  		if err != nil {
   367  			return nil, nil, errors.Trace(err)
   368  		}
   369  		attachments, err := stVolume.MachineVolumeAttachments(machineTag)
   370  		if err != nil {
   371  			return nil, nil, errors.Trace(err)
   372  		}
   373  		for _, attachment := range attachments {
   374  			volumeTag := attachment.Volume()
   375  			volumesByTag[volumeTag] = nil
   376  			volumeAttachments[volumeTag] = append(volumeAttachments[volumeTag], attachment)
   377  		}
   378  	}
   379  	for volumeTag := range volumesByTag {
   380  		volume, err := stVolume.Volume(volumeTag)
   381  		if err != nil {
   382  			return nil, nil, errors.Trace(err)
   383  		}
   384  		volumesByTag[volumeTag] = volume
   385  	}
   386  	volumes := make([]state.Volume, 0, len(volumesByTag))
   387  	for _, volume := range volumesByTag {
   388  		volumes = append(volumes, volume)
   389  	}
   390  	return volumes, volumeAttachments, nil
   391  }
   392  
   393  func (a *StorageAPI) createVolumeDetailsList(
   394  	volumes []state.Volume,
   395  	attachments map[names.VolumeTag][]state.VolumeAttachment,
   396  ) ([]params.VolumeDetails, error) {
   397  	if len(volumes) == 0 {
   398  		return nil, nil
   399  	}
   400  	results := make([]params.VolumeDetails, len(volumes))
   401  	for i, v := range volumes {
   402  		details, err := storagecommon.VolumeDetails(a.storageAccess, a.unitAssignedMachine, v, attachments[v.VolumeTag()])
   403  		if err != nil {
   404  			return nil, errors.Annotatef(
   405  				err, "getting details for %s",
   406  				names.ReadableString(v.VolumeTag()),
   407  			)
   408  		}
   409  		results[i] = *details
   410  	}
   411  	return results, nil
   412  }
   413  
   414  // ListFilesystems returns a list of filesystems in the environment matching
   415  // the provided filter. Each result describes a filesystem in detail, including
   416  // the filesystem's attachments.
   417  func (a *StorageAPI) ListFilesystems(filters params.FilesystemFilters) (params.FilesystemDetailsListResults, error) {
   418  	results := params.FilesystemDetailsListResults{
   419  		Results: make([]params.FilesystemDetailsListResult, len(filters.Filters)),
   420  	}
   421  	if err := a.checkCanRead(); err != nil {
   422  		return results, errors.Trace(err)
   423  	}
   424  
   425  	for i, filter := range filters.Filters {
   426  		filesystems, filesystemAttachments, err := filterFilesystems(a.storageAccess, filter)
   427  		if err != nil {
   428  			results.Results[i].Error = apiservererrors.ServerError(err)
   429  			continue
   430  		}
   431  		details, err := a.createFilesystemDetailsList(filesystems, filesystemAttachments)
   432  		if err != nil {
   433  			results.Results[i].Error = apiservererrors.ServerError(err)
   434  			continue
   435  		}
   436  		results.Results[i].Result = details
   437  	}
   438  	return results, nil
   439  }
   440  
   441  func filterFilesystems(
   442  	stFile storageFile,
   443  	f params.FilesystemFilter,
   444  ) ([]state.Filesystem, map[names.FilesystemTag][]state.FilesystemAttachment, error) {
   445  	if f.IsEmpty() {
   446  		// No filter was specified: get all filesystems, and all attachments.
   447  		filesystems, err := stFile.AllFilesystems()
   448  		if err != nil {
   449  			return nil, nil, errors.Trace(err)
   450  		}
   451  		filesystemAttachments := make(map[names.FilesystemTag][]state.FilesystemAttachment)
   452  		for _, f := range filesystems {
   453  			attachments, err := stFile.FilesystemAttachments(f.FilesystemTag())
   454  			if err != nil {
   455  				return nil, nil, errors.Trace(err)
   456  			}
   457  			filesystemAttachments[f.FilesystemTag()] = attachments
   458  		}
   459  		return filesystems, filesystemAttachments, nil
   460  	}
   461  	filesystemsByTag := make(map[names.FilesystemTag]state.Filesystem)
   462  	filesystemAttachments := make(map[names.FilesystemTag][]state.FilesystemAttachment)
   463  	for _, machine := range f.Machines {
   464  		machineTag, err := names.ParseMachineTag(machine)
   465  		if err != nil {
   466  			return nil, nil, errors.Trace(err)
   467  		}
   468  		attachments, err := stFile.MachineFilesystemAttachments(machineTag)
   469  		if err != nil {
   470  			return nil, nil, errors.Trace(err)
   471  		}
   472  		for _, attachment := range attachments {
   473  			filesystemTag := attachment.Filesystem()
   474  			filesystemsByTag[filesystemTag] = nil
   475  			filesystemAttachments[filesystemTag] = append(filesystemAttachments[filesystemTag], attachment)
   476  		}
   477  	}
   478  	for filesystemTag := range filesystemsByTag {
   479  		filesystem, err := stFile.Filesystem(filesystemTag)
   480  		if err != nil {
   481  			return nil, nil, errors.Trace(err)
   482  		}
   483  		filesystemsByTag[filesystemTag] = filesystem
   484  	}
   485  	filesystems := make([]state.Filesystem, 0, len(filesystemsByTag))
   486  	for _, filesystem := range filesystemsByTag {
   487  		filesystems = append(filesystems, filesystem)
   488  	}
   489  	return filesystems, filesystemAttachments, nil
   490  }
   491  
   492  func (a *StorageAPI) createFilesystemDetailsList(
   493  	filesystems []state.Filesystem,
   494  	attachments map[names.FilesystemTag][]state.FilesystemAttachment,
   495  ) ([]params.FilesystemDetails, error) {
   496  	if len(filesystems) == 0 {
   497  		return nil, nil
   498  	}
   499  	results := make([]params.FilesystemDetails, len(filesystems))
   500  	for i, f := range filesystems {
   501  		details, err := storagecommon.FilesystemDetails(a.storageAccess, a.unitAssignedMachine, f, attachments[f.FilesystemTag()])
   502  		if err != nil {
   503  			return nil, errors.Annotatef(
   504  				err, "getting details for %s",
   505  				names.ReadableString(f.FilesystemTag()),
   506  			)
   507  		}
   508  		results[i] = *details
   509  	}
   510  	return results, nil
   511  }
   512  
   513  // AddToUnit validates and creates additional storage instances for units.
   514  // A "CHANGE" block can block this operation.
   515  func (a *StorageAPI) AddToUnit(args params.StoragesAddParams) (params.AddStorageResults, error) {
   516  	return a.addToUnit(args)
   517  }
   518  
   519  func (a *StorageAPI) addToUnit(args params.StoragesAddParams) (params.AddStorageResults, error) {
   520  	if err := a.checkCanWrite(); err != nil {
   521  		return params.AddStorageResults{}, errors.Trace(err)
   522  	}
   523  
   524  	// Check if changes are allowed and the operation may proceed.
   525  	blockChecker := common.NewBlockChecker(a.backend)
   526  	if err := blockChecker.ChangeAllowed(); err != nil {
   527  		return params.AddStorageResults{}, errors.Trace(err)
   528  	}
   529  
   530  	paramsToState := func(p params.StorageConstraints) state.StorageConstraints {
   531  		s := state.StorageConstraints{Pool: p.Pool}
   532  		if p.Size != nil {
   533  			s.Size = *p.Size
   534  		}
   535  		if p.Count != nil {
   536  			s.Count = *p.Count
   537  		}
   538  		return s
   539  	}
   540  
   541  	result := make([]params.AddStorageResult, len(args.Storages))
   542  	for i, one := range args.Storages {
   543  		u, err := names.ParseUnitTag(one.UnitTag)
   544  		if err != nil {
   545  			result[i].Error = apiservererrors.ServerError(err)
   546  			continue
   547  		}
   548  
   549  		storageTags, err := a.storageAccess.AddStorageForUnit(
   550  			u, one.StorageName, paramsToState(one.Constraints),
   551  		)
   552  		if err != nil {
   553  			result[i].Error = apiservererrors.ServerError(err)
   554  		}
   555  		tagStrings := make([]string, len(storageTags))
   556  		for i, tag := range storageTags {
   557  			tagStrings[i] = tag.String()
   558  		}
   559  		result[i].Result = &params.AddStorageDetails{
   560  			StorageTags: tagStrings,
   561  		}
   562  	}
   563  	return params.AddStorageResults{Results: result}, nil
   564  }
   565  
   566  // Remove sets the specified storage entities to Dying, unless they are
   567  // already Dying or Dead, such that the storage will eventually be removed
   568  // from the model. If the arguments specify that the storage should be
   569  // destroyed, then the associated cloud storage will be destroyed first;
   570  // otherwise it will only be released from Juju's control.
   571  func (a *StorageAPI) Remove(args params.RemoveStorage) (params.ErrorResults, error) {
   572  	return a.remove(args)
   573  }
   574  
   575  func (a *StorageAPI) remove(args params.RemoveStorage) (params.ErrorResults, error) {
   576  	if err := a.checkCanWrite(); err != nil {
   577  		return params.ErrorResults{}, errors.Trace(err)
   578  	}
   579  
   580  	blockChecker := common.NewBlockChecker(a.backend)
   581  	if err := blockChecker.RemoveAllowed(); err != nil {
   582  		return params.ErrorResults{}, errors.Trace(err)
   583  	}
   584  
   585  	result := make([]params.ErrorResult, len(args.Storage))
   586  	for i, arg := range args.Storage {
   587  		tag, err := names.ParseStorageTag(arg.Tag)
   588  		if err != nil {
   589  			result[i].Error = apiservererrors.ServerError(err)
   590  			continue
   591  		}
   592  		remove := a.storageAccess.DestroyStorageInstance
   593  		if !arg.DestroyStorage {
   594  			remove = a.storageAccess.ReleaseStorageInstance
   595  		}
   596  		force := arg.Force != nil && *arg.Force
   597  		result[i].Error = apiservererrors.ServerError(remove(tag, arg.DestroyAttachments, force, common.MaxWait(arg.MaxWait)))
   598  	}
   599  	return params.ErrorResults{result}, nil
   600  }
   601  
   602  // DetachStorage sets the specified storage attachments to Dying, unless they are
   603  // already Dying or Dead. Any associated, persistent storage will remain
   604  // alive. This call can be forced.
   605  func (a *StorageAPI) DetachStorage(args params.StorageDetachmentParams) (params.ErrorResults, error) {
   606  	return a.internalDetach(args.StorageIds, args.Force, args.MaxWait)
   607  }
   608  
   609  func (a *StorageAPI) internalDetach(args params.StorageAttachmentIds, force *bool, maxWait *time.Duration) (params.ErrorResults, error) {
   610  	if err := a.checkCanWrite(); err != nil {
   611  		return params.ErrorResults{}, errors.Trace(err)
   612  	}
   613  
   614  	blockChecker := common.NewBlockChecker(a.backend)
   615  	if err := blockChecker.ChangeAllowed(); err != nil {
   616  		return params.ErrorResults{}, errors.Trace(err)
   617  	}
   618  
   619  	detachOne := func(arg params.StorageAttachmentId) error {
   620  		storageTag, err := names.ParseStorageTag(arg.StorageTag)
   621  		if err != nil {
   622  			return err
   623  		}
   624  		var unitTag names.UnitTag
   625  		if arg.UnitTag != "" {
   626  			var err error
   627  			unitTag, err = names.ParseUnitTag(arg.UnitTag)
   628  			if err != nil {
   629  				return err
   630  			}
   631  		}
   632  		return a.detachStorage(storageTag, unitTag, force, maxWait)
   633  	}
   634  
   635  	result := make([]params.ErrorResult, len(args.Ids))
   636  	for i, arg := range args.Ids {
   637  		result[i].Error = apiservererrors.ServerError(detachOne(arg))
   638  	}
   639  	return params.ErrorResults{result}, nil
   640  }
   641  
   642  func (a *StorageAPI) detachStorage(storageTag names.StorageTag, unitTag names.UnitTag, force *bool, maxWait *time.Duration) error {
   643  	forcing := force != nil && *force
   644  	if unitTag != (names.UnitTag{}) {
   645  		// The caller has specified a unit explicitly. Do
   646  		// not filter out "not found" errors in this case.
   647  		return a.storageAccess.DetachStorage(storageTag, unitTag, forcing, common.MaxWait(maxWait))
   648  	}
   649  	attachments, err := a.storageAccess.StorageAttachments(storageTag)
   650  	if err != nil {
   651  		return errors.Trace(err)
   652  	}
   653  	if len(attachments) == 0 {
   654  		// No attachments: check if the storage exists at all.
   655  		if _, err := a.storageAccess.StorageInstance(storageTag); err != nil {
   656  			return errors.Trace(err)
   657  		}
   658  	}
   659  	for _, att := range attachments {
   660  		if att.Life() != state.Alive {
   661  			continue
   662  		}
   663  		err := a.storageAccess.DetachStorage(storageTag, att.Unit(), forcing, common.MaxWait(maxWait))
   664  		if err != nil && !errors.IsNotFound(err) {
   665  			// We only care about NotFound errors if
   666  			// the user specified a unit explicitly.
   667  			return errors.Trace(err)
   668  		}
   669  	}
   670  	return nil
   671  }
   672  
   673  // Attach attaches existing storage instances to units.
   674  // A "CHANGE" block can block this operation.
   675  func (a *StorageAPI) Attach(args params.StorageAttachmentIds) (params.ErrorResults, error) {
   676  	if err := a.checkCanWrite(); err != nil {
   677  		return params.ErrorResults{}, errors.Trace(err)
   678  	}
   679  
   680  	blockChecker := common.NewBlockChecker(a.backend)
   681  	if err := blockChecker.ChangeAllowed(); err != nil {
   682  		return params.ErrorResults{}, errors.Trace(err)
   683  	}
   684  
   685  	attachOne := func(arg params.StorageAttachmentId) error {
   686  		storageTag, err := names.ParseStorageTag(arg.StorageTag)
   687  		if err != nil {
   688  			return err
   689  		}
   690  		unitTag, err := names.ParseUnitTag(arg.UnitTag)
   691  		if err != nil {
   692  			return err
   693  		}
   694  		return a.storageAccess.AttachStorage(storageTag, unitTag)
   695  	}
   696  
   697  	result := make([]params.ErrorResult, len(args.Ids))
   698  	for i, arg := range args.Ids {
   699  		result[i].Error = apiservererrors.ServerError(attachOne(arg))
   700  	}
   701  	return params.ErrorResults{Results: result}, nil
   702  }
   703  
   704  // Import imports existing storage into the model.
   705  // A "CHANGE" block can block this operation.
   706  func (a *StorageAPI) Import(args params.BulkImportStorageParams) (params.ImportStorageResults, error) {
   707  	if err := a.checkCanWrite(); err != nil {
   708  		return params.ImportStorageResults{}, errors.Trace(err)
   709  	}
   710  
   711  	blockChecker := common.NewBlockChecker(a.backend)
   712  	if err := blockChecker.ChangeAllowed(); err != nil {
   713  		return params.ImportStorageResults{}, errors.Trace(err)
   714  	}
   715  
   716  	results := make([]params.ImportStorageResult, len(args.Storage))
   717  	for i, arg := range args.Storage {
   718  		details, err := a.importStorage(arg)
   719  		if err != nil {
   720  			results[i].Error = apiservererrors.ServerError(err)
   721  			continue
   722  		}
   723  		results[i].Result = details
   724  	}
   725  	return params.ImportStorageResults{Results: results}, nil
   726  }
   727  
   728  func (a *StorageAPI) importStorage(arg params.ImportStorageParams) (*params.ImportStorageDetails, error) {
   729  	if arg.Kind != params.StorageKindFilesystem {
   730  		// TODO(axw) implement support for volumes.
   731  		return nil, errors.NotSupportedf("storage kind %q", arg.Kind.String())
   732  	}
   733  	if !storage.IsValidPoolName(arg.Pool) {
   734  		return nil, errors.NotValidf("pool name %q", arg.Pool)
   735  	}
   736  
   737  	pm, registry, err := a.storageMetadata()
   738  	if err != nil {
   739  		return nil, errors.Trace(err)
   740  	}
   741  
   742  	cfg, err := pm.Get(arg.Pool)
   743  	if errors.IsNotFound(err) {
   744  		cfg, err = storage.NewConfig(
   745  			arg.Pool,
   746  			storage.ProviderType(arg.Pool),
   747  			map[string]interface{}{},
   748  		)
   749  		if err != nil {
   750  			return nil, errors.Trace(err)
   751  		}
   752  	} else if err != nil {
   753  		return nil, errors.Trace(err)
   754  	}
   755  	provider, err := registry.StorageProvider(cfg.Provider())
   756  	if err != nil {
   757  		return nil, errors.Trace(err)
   758  	}
   759  	return a.importFilesystem(arg, provider, cfg)
   760  }
   761  
   762  func (a *StorageAPI) importFilesystem(
   763  	arg params.ImportStorageParams,
   764  	provider storage.Provider,
   765  	cfg *storage.Config,
   766  ) (*params.ImportStorageDetails, error) {
   767  	resourceTags := map[string]string{
   768  		tags.JujuModel:      a.backend.ModelTag().Id(),
   769  		tags.JujuController: a.backend.ControllerTag().Id(),
   770  	}
   771  	var volumeInfo *state.VolumeInfo
   772  	filesystemInfo := state.FilesystemInfo{Pool: arg.Pool}
   773  
   774  	// If the storage provider supports filesystems, import the filesystem,
   775  	// otherwise import a volume which will back a filesystem.
   776  	if provider.Supports(storage.StorageKindFilesystem) {
   777  		filesystemSource, err := provider.FilesystemSource(cfg)
   778  		if err != nil {
   779  			return nil, errors.Trace(err)
   780  		}
   781  		filesystemImporter, ok := filesystemSource.(storage.FilesystemImporter)
   782  		if !ok {
   783  			return nil, errors.NotSupportedf(
   784  				"importing filesystem with storage provider %q",
   785  				cfg.Provider(),
   786  			)
   787  		}
   788  		info, err := filesystemImporter.ImportFilesystem(a.callContext, arg.ProviderId, resourceTags)
   789  		if err != nil {
   790  			return nil, errors.Annotate(err, "importing filesystem")
   791  		}
   792  		filesystemInfo.FilesystemId = arg.ProviderId
   793  		filesystemInfo.Size = info.Size
   794  	} else {
   795  		volumeSource, err := provider.VolumeSource(cfg)
   796  		if err != nil {
   797  			return nil, errors.Trace(err)
   798  		}
   799  		volumeImporter, ok := volumeSource.(storage.VolumeImporter)
   800  		if !ok {
   801  			return nil, errors.NotSupportedf(
   802  				"importing volume with storage provider %q",
   803  				cfg.Provider(),
   804  			)
   805  		}
   806  		info, err := volumeImporter.ImportVolume(a.callContext, arg.ProviderId, resourceTags)
   807  		if err != nil {
   808  			return nil, errors.Annotate(err, "importing volume")
   809  		}
   810  		volumeInfo = &state.VolumeInfo{
   811  			HardwareId: info.HardwareId,
   812  			WWN:        info.WWN,
   813  			Size:       info.Size,
   814  			Pool:       arg.Pool,
   815  			VolumeId:   info.VolumeId,
   816  			Persistent: info.Persistent,
   817  		}
   818  		filesystemInfo.Size = info.Size
   819  	}
   820  
   821  	storageTag, err := a.storageAccess.AddExistingFilesystem(filesystemInfo, volumeInfo, arg.StorageName)
   822  	if err != nil {
   823  		return nil, errors.Trace(err)
   824  	}
   825  	return &params.ImportStorageDetails{
   826  		StorageTag: storageTag.String(),
   827  	}, nil
   828  }
   829  
   830  // RemovePool deletes the named pool
   831  func (a *StorageAPI) RemovePool(p params.StoragePoolDeleteArgs) (params.ErrorResults, error) {
   832  	results := params.ErrorResults{
   833  		Results: make([]params.ErrorResult, len(p.Pools)),
   834  	}
   835  	if err := a.checkCanWrite(); err != nil {
   836  		return results, errors.Trace(err)
   837  	}
   838  	for i, pool := range p.Pools {
   839  		err := a.storageAccess.RemoveStoragePool(pool.Name)
   840  		if err != nil {
   841  			results.Results[i].Error = apiservererrors.ServerError(err)
   842  		}
   843  	}
   844  	return results, nil
   845  }
   846  
   847  // UpdatePool deletes the named pool
   848  func (a *StorageAPI) UpdatePool(p params.StoragePoolArgs) (params.ErrorResults, error) {
   849  	results := params.ErrorResults{
   850  		Results: make([]params.ErrorResult, len(p.Pools)),
   851  	}
   852  	if err := a.checkCanWrite(); err != nil {
   853  		return results, errors.Trace(err)
   854  	}
   855  	pm, _, err := a.storageMetadata()
   856  	if err != nil {
   857  		return results, errors.Trace(err)
   858  	}
   859  
   860  	for i, pool := range p.Pools {
   861  		err := pm.Replace(pool.Name, pool.Provider, pool.Attrs)
   862  		if err != nil {
   863  			results.Results[i].Error = apiservererrors.ServerError(err)
   864  		}
   865  	}
   866  	return results, nil
   867  }
   868  
   869  // unitAssignedMachine returns the tag of the machine that the unit
   870  // is assigned to, or an error if the unit cannot be obtained or is
   871  // not assigned to a machine.
   872  func (a *StorageAPI) unitAssignedMachine(tag names.UnitTag) (names.MachineTag, error) {
   873  	unit, err := a.backend.Unit(tag.Id())
   874  	if err != nil {
   875  		return names.MachineTag{}, errors.Trace(err)
   876  	}
   877  	mid, err := unit.AssignedMachineId()
   878  	if err != nil {
   879  		return names.MachineTag{}, errors.Trace(err)
   880  	}
   881  	return names.NewMachineTag(mid), nil
   882  }