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 }