github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/storage/client.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  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/api/base"
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/storage"
    13  )
    14  
    15  // Client allows access to the storage API end point.
    16  type Client struct {
    17  	base.ClientFacade
    18  	facade base.FacadeCaller
    19  }
    20  
    21  // NewClient creates a new client for accessing the storage API.
    22  func NewClient(st base.APICallCloser) *Client {
    23  	frontend, backend := base.NewClientFacade(st, "Storage")
    24  	return &Client{ClientFacade: frontend, facade: backend}
    25  }
    26  
    27  // StorageDetails retrieves details about desired storage instances.
    28  func (c *Client) StorageDetails(tags []names.StorageTag) ([]params.StorageDetailsResult, error) {
    29  	found := params.StorageDetailsResults{}
    30  	entities := make([]params.Entity, len(tags))
    31  	for i, tag := range tags {
    32  		entities[i] = params.Entity{Tag: tag.String()}
    33  	}
    34  	if err := c.facade.FacadeCall("StorageDetails", params.Entities{Entities: entities}, &found); err != nil {
    35  		return nil, errors.Trace(err)
    36  	}
    37  	return found.Results, nil
    38  }
    39  
    40  // ListStorageDetails lists all storage.
    41  func (c *Client) ListStorageDetails() ([]params.StorageDetails, error) {
    42  	args := params.StorageFilters{
    43  		[]params.StorageFilter{{}}, // one empty filter
    44  	}
    45  	var results params.StorageDetailsListResults
    46  	if err := c.facade.FacadeCall("ListStorageDetails", args, &results); err != nil {
    47  		return nil, errors.Trace(err)
    48  	}
    49  	if len(results.Results) != 1 {
    50  		return nil, errors.Errorf(
    51  			"expected 1 result, got %d",
    52  			len(results.Results),
    53  		)
    54  	}
    55  	if results.Results[0].Error != nil {
    56  		return nil, errors.Trace(results.Results[0].Error)
    57  	}
    58  	return results.Results[0].Result, nil
    59  }
    60  
    61  // ListPools returns a list of pools that matches given filter.
    62  // If no filter was provided, a list of all pools is returned.
    63  func (c *Client) ListPools(providers, names []string) ([]params.StoragePool, error) {
    64  	args := params.StoragePoolFilters{
    65  		Filters: []params.StoragePoolFilter{{
    66  			Names:     names,
    67  			Providers: providers,
    68  		}},
    69  	}
    70  	var results params.StoragePoolsResults
    71  	if err := c.facade.FacadeCall("ListPools", args, &results); err != nil {
    72  		return nil, errors.Trace(err)
    73  	}
    74  	if len(results.Results) != 1 {
    75  		return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
    76  	}
    77  	if err := results.Results[0].Error; err != nil {
    78  		return nil, err
    79  	}
    80  	return results.Results[0].Result, nil
    81  }
    82  
    83  // CreatePool creates pool with specified parameters.
    84  func (c *Client) CreatePool(pname, provider string, attrs map[string]interface{}) error {
    85  	// Older facade did not support bulk calls.
    86  	if c.BestAPIVersion() < 5 {
    87  		args := params.StoragePool{
    88  			Name:     pname,
    89  			Provider: provider,
    90  			Attrs:    attrs,
    91  		}
    92  		return c.facade.FacadeCall("CreatePool", args, nil)
    93  	}
    94  
    95  	var results params.ErrorResults
    96  	args := params.StoragePoolArgs{
    97  		Pools: []params.StoragePool{{
    98  			Name:     pname,
    99  			Provider: provider,
   100  			Attrs:    attrs,
   101  		}},
   102  	}
   103  
   104  	if err := c.facade.FacadeCall("CreatePool", args, &results); err != nil {
   105  		return errors.Trace(err)
   106  	}
   107  	return results.OneError()
   108  }
   109  
   110  // RemovePool removes the named pool
   111  func (c *Client) RemovePool(pname string) error {
   112  	if c.BestAPIVersion() < 5 {
   113  		return errors.New("removing storage pools is not supported by this version of Juju")
   114  	}
   115  	var results params.ErrorResults
   116  	args := params.StoragePoolDeleteArgs{
   117  		Pools: []params.StoragePoolDeleteArg{{
   118  			Name: pname,
   119  		}},
   120  	}
   121  	if err := c.facade.FacadeCall("RemovePool", args, &results); err != nil {
   122  		return errors.Trace(err)
   123  	}
   124  	return results.OneError()
   125  }
   126  
   127  // UpdatePool updates a  pool with specified parameters.
   128  func (c *Client) UpdatePool(pname, provider string, attrs map[string]interface{}) error {
   129  	if c.BestAPIVersion() < 5 {
   130  		return errors.New("updating storage pools is not supported by this version of Juju")
   131  	}
   132  	var results params.ErrorResults
   133  	args := params.StoragePoolArgs{
   134  		Pools: []params.StoragePool{{
   135  			Name:     pname,
   136  			Provider: provider,
   137  			Attrs:    attrs,
   138  		}},
   139  	}
   140  	if err := c.facade.FacadeCall("UpdatePool", args, &results); err != nil {
   141  		return errors.Trace(err)
   142  	}
   143  	return results.OneError()
   144  }
   145  
   146  // ListVolumes lists volumes for desired machines.
   147  // If no machines provided, a list of all volumes is returned.
   148  func (c *Client) ListVolumes(machines []string) ([]params.VolumeDetailsListResult, error) {
   149  	filters := make([]params.VolumeFilter, len(machines))
   150  	for i, machine := range machines {
   151  		filters[i].Machines = []string{names.NewMachineTag(machine).String()}
   152  	}
   153  	if len(filters) == 0 {
   154  		filters = []params.VolumeFilter{{}}
   155  	}
   156  	args := params.VolumeFilters{filters}
   157  	var results params.VolumeDetailsListResults
   158  	if err := c.facade.FacadeCall("ListVolumes", args, &results); err != nil {
   159  		return nil, errors.Trace(err)
   160  	}
   161  	if len(results.Results) != len(filters) {
   162  		return nil, errors.Errorf(
   163  			"expected %d result(s), got %d",
   164  			len(filters), len(results.Results),
   165  		)
   166  	}
   167  	return results.Results, nil
   168  }
   169  
   170  // ListFilesystems lists filesystems for desired machines.
   171  // If no machines provided, a list of all filesystems is returned.
   172  func (c *Client) ListFilesystems(machines []string) ([]params.FilesystemDetailsListResult, error) {
   173  	filters := make([]params.FilesystemFilter, len(machines))
   174  	for i, machine := range machines {
   175  		filters[i].Machines = []string{names.NewMachineTag(machine).String()}
   176  	}
   177  	if len(filters) == 0 {
   178  		filters = []params.FilesystemFilter{{}}
   179  	}
   180  	args := params.FilesystemFilters{filters}
   181  	var results params.FilesystemDetailsListResults
   182  	if err := c.facade.FacadeCall("ListFilesystems", args, &results); err != nil {
   183  		return nil, errors.Trace(err)
   184  	}
   185  	if len(results.Results) != len(filters) {
   186  		return nil, errors.Errorf(
   187  			"expected %d result(s), got %d",
   188  			len(filters), len(results.Results),
   189  		)
   190  	}
   191  	return results.Results, nil
   192  }
   193  
   194  // AddToUnit adds specified storage to desired units.
   195  //
   196  // NOTE(axw) for old controllers, the results will only
   197  // contain errors.
   198  func (c *Client) AddToUnit(storages []params.StorageAddParams) ([]params.AddStorageResult, error) {
   199  	out := params.AddStorageResults{}
   200  	in := params.StoragesAddParams{Storages: storages}
   201  	err := c.facade.FacadeCall("AddToUnit", in, &out)
   202  	if err != nil {
   203  		return nil, errors.Trace(err)
   204  	}
   205  	return out.Results, nil
   206  }
   207  
   208  // Attach attaches existing storage to a unit.
   209  func (c *Client) Attach(unitId string, storageIds []string) ([]params.ErrorResult, error) {
   210  	in := params.StorageAttachmentIds{
   211  		make([]params.StorageAttachmentId, len(storageIds)),
   212  	}
   213  	if !names.IsValidUnit(unitId) {
   214  		return nil, errors.NotValidf("unit ID %q", unitId)
   215  	}
   216  	for i, storageId := range storageIds {
   217  		if !names.IsValidStorage(storageId) {
   218  			return nil, errors.NotValidf("storage ID %q", storageId)
   219  		}
   220  		in.Ids[i] = params.StorageAttachmentId{
   221  			StorageTag: names.NewStorageTag(storageId).String(),
   222  			UnitTag:    names.NewUnitTag(unitId).String(),
   223  		}
   224  	}
   225  	out := params.ErrorResults{}
   226  	if err := c.facade.FacadeCall("Attach", in, &out); err != nil {
   227  		return nil, errors.Trace(err)
   228  	}
   229  	if len(out.Results) != len(storageIds) {
   230  		return nil, errors.Errorf(
   231  			"expected %d result(s), got %d",
   232  			len(storageIds), len(out.Results),
   233  		)
   234  	}
   235  	return out.Results, nil
   236  }
   237  
   238  // Remove removes the specified storage entities from the model,
   239  // optionally destroying them.
   240  func (c *Client) Remove(storageIds []string, destroyAttachments, destroyStorage bool) ([]params.ErrorResult, error) {
   241  	for _, id := range storageIds {
   242  		if !names.IsValidStorage(id) {
   243  			return nil, errors.NotValidf("storage ID %q", id)
   244  		}
   245  	}
   246  	results := params.ErrorResults{}
   247  	var args interface{}
   248  	var method string
   249  	if c.BestAPIVersion() <= 3 {
   250  		if !destroyStorage {
   251  			return nil, errors.Errorf("this juju controller does not support non-destructive removal of storage")
   252  		}
   253  		// In version 3, destroyAttached is ignored; removing
   254  		// storage always causes detachment.
   255  		entities := make([]params.Entity, len(storageIds))
   256  		for i, id := range storageIds {
   257  			entities[i].Tag = names.NewStorageTag(id).String()
   258  		}
   259  		args = params.Entities{entities}
   260  		method = "Destroy"
   261  	} else {
   262  		storage := make([]params.RemoveStorageInstance, len(storageIds))
   263  		for i, id := range storageIds {
   264  			storage[i] = params.RemoveStorageInstance{
   265  				Tag:                names.NewStorageTag(id).String(),
   266  				DestroyAttachments: destroyAttachments,
   267  				DestroyStorage:     destroyStorage,
   268  			}
   269  		}
   270  		args = params.RemoveStorage{storage}
   271  		method = "Remove"
   272  	}
   273  	if err := c.facade.FacadeCall(method, args, &results); err != nil {
   274  		return nil, errors.Trace(err)
   275  	}
   276  	if len(results.Results) != len(storageIds) {
   277  		return nil, errors.Errorf(
   278  			"expected %d result(s), got %d",
   279  			len(storageIds), len(results.Results),
   280  		)
   281  	}
   282  	return results.Results, nil
   283  }
   284  
   285  // Detach detaches the specified storage entities.
   286  func (c *Client) Detach(storageIds []string) ([]params.ErrorResult, error) {
   287  	results := params.ErrorResults{}
   288  	args := make([]params.StorageAttachmentId, len(storageIds))
   289  	for i, id := range storageIds {
   290  		if !names.IsValidStorage(id) {
   291  			return nil, errors.NotValidf("storage ID %q", id)
   292  		}
   293  		args[i] = params.StorageAttachmentId{
   294  			StorageTag: names.NewStorageTag(id).String(),
   295  		}
   296  	}
   297  	if err := c.facade.FacadeCall(
   298  		"Detach",
   299  		params.StorageAttachmentIds{args},
   300  		&results,
   301  	); err != nil {
   302  		return nil, errors.Trace(err)
   303  	}
   304  	if len(results.Results) != len(storageIds) {
   305  		return nil, errors.Errorf(
   306  			"expected %d result(s), got %d",
   307  			len(storageIds), len(results.Results),
   308  		)
   309  	}
   310  	return results.Results, nil
   311  }
   312  
   313  // Import imports storage into the model.
   314  func (c *Client) Import(
   315  	kind storage.StorageKind,
   316  	storagePool string,
   317  	storageProviderId string,
   318  	storageName string,
   319  ) (names.StorageTag, error) {
   320  	var results params.ImportStorageResults
   321  	args := params.BulkImportStorageParams{
   322  		[]params.ImportStorageParams{{
   323  			StorageName: storageName,
   324  			Kind:        params.StorageKind(kind),
   325  			Pool:        storagePool,
   326  			ProviderId:  storageProviderId,
   327  		}},
   328  	}
   329  	if err := c.facade.FacadeCall("Import", args, &results); err != nil {
   330  		return names.StorageTag{}, errors.Trace(err)
   331  	}
   332  	if len(results.Results) != 1 {
   333  		return names.StorageTag{}, errors.Errorf(
   334  			"expected 1 result, got %d",
   335  			len(results.Results),
   336  		)
   337  	}
   338  	if err := results.Results[0].Error; err != nil {
   339  		return names.StorageTag{}, err
   340  	}
   341  	return names.ParseStorageTag(results.Results[0].Result.StorageTag)
   342  }