github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 provides an API server facade for managing 5 // storage entities. 6 package storage 7 8 import ( 9 "github.com/juju/collections/set" 10 "github.com/juju/errors" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/apiserver/common" 14 "github.com/juju/juju/apiserver/common/storagecommon" 15 "github.com/juju/juju/apiserver/facade" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/caas" 18 "github.com/juju/juju/core/status" 19 "github.com/juju/juju/environs" 20 "github.com/juju/juju/environs/context" 21 "github.com/juju/juju/environs/tags" 22 "github.com/juju/juju/permission" 23 "github.com/juju/juju/state" 24 "github.com/juju/juju/state/stateenvirons" 25 "github.com/juju/juju/storage" 26 "github.com/juju/juju/storage/poolmanager" 27 ) 28 29 // StorageAPI implements the latest version (v5) of the Storage API which adds Update and Delete. 30 type StorageAPI struct { 31 backend backend 32 storageAccess storageAccess 33 registry storage.ProviderRegistry 34 poolManager poolmanager.PoolManager 35 authorizer facade.Authorizer 36 callContext context.ProviderCallContext 37 modelType state.ModelType 38 } 39 40 // APIv4 implements the storage v4 API adding AddToUnit, Import and Remove (replacing Destroy) 41 type StorageAPIv4 struct { 42 StorageAPI 43 } 44 45 // APIv3 implements the storage v3 API. 46 type StorageAPIv3 struct { 47 StorageAPIv4 48 } 49 50 // NewStorageAPI returns a new storage API facade. 51 func NewStorageAPI(context facade.Context) (*StorageAPI, error) { 52 st := context.State() 53 model, err := st.Model() 54 if err != nil { 55 return nil, errors.Trace(err) 56 } 57 registry, err := stateenvirons.NewStorageProviderRegistryForModel( 58 model, 59 stateenvirons.GetNewEnvironFunc(environs.New), 60 stateenvirons.GetNewCAASBrokerFunc(caas.New)) 61 if err != nil { 62 return nil, errors.Trace(err) 63 } 64 pm := poolmanager.New(state.NewStateSettings(st), registry) 65 66 storageAccessor, err := getStorageAccessor(st) 67 if err != nil { 68 return nil, errors.Annotate(err, "getting backend") 69 } 70 71 authorizer := context.Auth() 72 if !authorizer.AuthClient() { 73 return nil, common.ErrPerm 74 } 75 return newStorageAPI(stateShim{st}, model.Type(), storageAccessor, registry, pm, authorizer, state.CallContext(st)), nil 76 } 77 78 func newStorageAPI( 79 backend backend, 80 modelType state.ModelType, 81 storageAccess storageAccess, 82 registry storage.ProviderRegistry, 83 pm poolmanager.PoolManager, 84 authorizer facade.Authorizer, 85 callContext context.ProviderCallContext, 86 ) *StorageAPI { 87 return &StorageAPI{ 88 backend: backend, 89 modelType: modelType, 90 storageAccess: storageAccess, 91 registry: registry, 92 poolManager: pm, 93 authorizer: authorizer, 94 callContext: callContext, 95 } 96 } 97 98 // NewStorageAPIV4 returns a new storage v4 API facade. 99 func NewStorageAPIV4(context facade.Context) (*StorageAPIv4, error) { 100 storageAPI, err := NewStorageAPI(context) 101 if err != nil { 102 return nil, err 103 } 104 return &StorageAPIv4{ 105 StorageAPI: *storageAPI, 106 }, nil 107 } 108 109 // NewStorageAPIV3 returns a new storage v3 API facade. 110 func NewStorageAPIV3(context facade.Context) (*StorageAPIv3, error) { 111 storageAPI, err := NewStorageAPIV4(context) 112 if err != nil { 113 return nil, err 114 } 115 return &StorageAPIv3{ 116 StorageAPIv4: *storageAPI, 117 }, nil 118 } 119 120 func (a *StorageAPI) checkCanRead() error { 121 canRead, err := a.authorizer.HasPermission(permission.ReadAccess, a.backend.ModelTag()) 122 if err != nil { 123 return errors.Trace(err) 124 } 125 if !canRead { 126 return common.ErrPerm 127 } 128 return nil 129 } 130 131 func (a *StorageAPI) checkCanWrite() error { 132 canWrite, err := a.authorizer.HasPermission(permission.WriteAccess, a.backend.ModelTag()) 133 if err != nil { 134 return errors.Trace(err) 135 } 136 if !canWrite { 137 return common.ErrPerm 138 } 139 return nil 140 } 141 142 // StorageDetails retrieves and returns detailed information about desired 143 // storage identified by supplied tags. If specified storage cannot be 144 // retrieved, individual error is returned instead of storage information. 145 func (a *StorageAPI) StorageDetails(entities params.Entities) (params.StorageDetailsResults, error) { 146 if err := a.checkCanWrite(); err != nil { 147 return params.StorageDetailsResults{}, errors.Trace(err) 148 } 149 results := make([]params.StorageDetailsResult, len(entities.Entities)) 150 for i, entity := range entities.Entities { 151 storageTag, err := names.ParseStorageTag(entity.Tag) 152 if err != nil { 153 results[i].Error = common.ServerError(err) 154 continue 155 } 156 storageInstance, err := a.storageAccess.StorageInstance(storageTag) 157 if err != nil { 158 results[i].Error = common.ServerError(err) 159 continue 160 } 161 details, err := createStorageDetails(a.backend, a.storageAccess, storageInstance) 162 if err != nil { 163 results[i].Error = common.ServerError(err) 164 continue 165 } 166 results[i].Result = details 167 } 168 return params.StorageDetailsResults{Results: results}, nil 169 } 170 171 // ListStorageDetails returns storage matching a filter. 172 func (a *StorageAPI) ListStorageDetails(filters params.StorageFilters) (params.StorageDetailsListResults, error) { 173 if err := a.checkCanRead(); err != nil { 174 return params.StorageDetailsListResults{}, errors.Trace(err) 175 } 176 results := params.StorageDetailsListResults{ 177 Results: make([]params.StorageDetailsListResult, len(filters.Filters)), 178 } 179 for i, filter := range filters.Filters { 180 list, err := a.listStorageDetails(filter) 181 if err != nil { 182 results.Results[i].Error = common.ServerError(err) 183 continue 184 } 185 results.Results[i].Result = list 186 } 187 return results, nil 188 } 189 190 func (a *StorageAPI) listStorageDetails(filter params.StorageFilter) ([]params.StorageDetails, error) { 191 if filter != (params.StorageFilter{}) { 192 // StorageFilter has no fields at the time of writing, but 193 // check that no fields are set in case we forget to update 194 // this code. 195 return nil, errors.NotSupportedf("storage filters") 196 } 197 stateInstances, err := a.storageAccess.AllStorageInstances() 198 if err != nil { 199 return nil, common.ServerError(err) 200 } 201 results := make([]params.StorageDetails, len(stateInstances)) 202 for i, stateInstance := range stateInstances { 203 details, err := createStorageDetails(a.backend, a.storageAccess, stateInstance) 204 if err != nil { 205 return nil, errors.Annotatef( 206 err, "getting details for %s", 207 names.ReadableString(stateInstance.Tag()), 208 ) 209 } 210 results[i] = *details 211 } 212 return results, nil 213 } 214 215 func createStorageDetails( 216 backend backend, 217 st storageAccess, 218 si state.StorageInstance, 219 ) (*params.StorageDetails, error) { 220 // Get information from underlying volume or filesystem. 221 var persistent bool 222 var statusEntity status.StatusGetter 223 if si.Kind() == state.StorageKindFilesystem { 224 stFile := st.FilesystemAccess() 225 if stFile == nil { 226 return nil, errors.NotImplementedf("FilesystemStorage instance") 227 } 228 // TODO(axw) when we support persistent filesystems, 229 // e.g. CephFS, we'll need to do set "persistent" 230 // here too. 231 filesystem, err := stFile.StorageInstanceFilesystem(si.StorageTag()) 232 if err != nil { 233 return nil, errors.Trace(err) 234 } 235 statusEntity = filesystem 236 } else { 237 stVolume := st.VolumeAccess() 238 if stVolume == nil { 239 return nil, errors.NotImplementedf("BlockStorage instance") 240 } 241 volume, err := stVolume.StorageInstanceVolume(si.StorageTag()) 242 if err != nil { 243 return nil, errors.Trace(err) 244 } 245 if info, err := volume.Info(); err == nil { 246 persistent = info.Persistent 247 } 248 statusEntity = volume 249 } 250 status, err := statusEntity.Status() 251 if err != nil { 252 return nil, errors.Trace(err) 253 } 254 255 // Get unit storage attachments. 256 var storageAttachmentDetails map[string]params.StorageAttachmentDetails 257 storageAttachments, err := st.StorageAttachments(si.StorageTag()) 258 if err != nil { 259 return nil, errors.Trace(err) 260 } 261 if len(storageAttachments) > 0 { 262 storageAttachmentDetails = make(map[string]params.StorageAttachmentDetails) 263 for _, a := range storageAttachments { 264 // TODO(caas) - handle attachments to units 265 machineTag, location, err := storageAttachmentInfo(backend, st, a) 266 if err != nil { 267 return nil, errors.Trace(err) 268 } 269 details := params.StorageAttachmentDetails{ 270 StorageTag: a.StorageInstance().String(), 271 UnitTag: a.Unit().String(), 272 Location: location, 273 Life: params.Life(a.Life().String()), 274 } 275 if machineTag.Id() != "" { 276 details.MachineTag = machineTag.String() 277 } 278 storageAttachmentDetails[a.Unit().String()] = details 279 } 280 } 281 282 var ownerTag string 283 if owner, ok := si.Owner(); ok { 284 ownerTag = owner.String() 285 } 286 287 return ¶ms.StorageDetails{ 288 StorageTag: si.Tag().String(), 289 OwnerTag: ownerTag, 290 Kind: params.StorageKind(si.Kind()), 291 Life: params.Life(si.Life().String()), 292 Status: common.EntityStatusFromState(status), 293 Persistent: persistent, 294 Attachments: storageAttachmentDetails, 295 }, nil 296 } 297 298 func storageAttachmentInfo( 299 backend backend, 300 st storageAccess, 301 a state.StorageAttachment, 302 ) (_ names.MachineTag, location string, _ error) { 303 machineTag, err := unitAssignedMachine(backend, a.Unit()) 304 if errors.IsNotAssigned(err) { 305 return names.MachineTag{}, "", nil 306 } else if err != nil { 307 return names.MachineTag{}, "", errors.Trace(err) 308 } 309 info, err := storagecommon.StorageAttachmentInfo(st, st.VolumeAccess(), st.FilesystemAccess(), a, machineTag) 310 if errors.IsNotProvisioned(err) { 311 return machineTag, "", nil 312 } else if err != nil { 313 return names.MachineTag{}, "", errors.Trace(err) 314 } 315 return machineTag, info.Location, nil 316 } 317 318 // ListPools returns a list of pools. 319 // If filter is provided, returned list only contains pools that match 320 // the filter. 321 // Pools can be filtered on names and provider types. 322 // If both names and types are provided as filter, 323 // pools that match either are returned. 324 // This method lists union of pools and environment provider types. 325 // If no filter is provided, all pools are returned. 326 func (a *StorageAPI) ListPools( 327 filters params.StoragePoolFilters, 328 ) (params.StoragePoolsResults, error) { 329 if err := a.checkCanRead(); err != nil { 330 return params.StoragePoolsResults{}, errors.Trace(err) 331 } 332 333 results := params.StoragePoolsResults{ 334 Results: make([]params.StoragePoolsResult, len(filters.Filters)), 335 } 336 for i, filter := range filters.Filters { 337 pools, err := a.listPools(a.ensureStoragePoolFilter(filter)) 338 if err != nil { 339 results.Results[i].Error = common.ServerError(err) 340 continue 341 } 342 results.Results[i].Result = pools 343 } 344 return results, nil 345 } 346 347 func (a *StorageAPI) ensureStoragePoolFilter(filter params.StoragePoolFilter) params.StoragePoolFilter { 348 if a.modelType == state.ModelTypeCAAS { 349 filter.Providers = append(filter.Providers, "kubernetes") 350 } 351 return filter 352 } 353 354 func (a *StorageAPI) listPools(filter params.StoragePoolFilter) ([]params.StoragePool, error) { 355 if err := a.validatePoolListFilter(filter); err != nil { 356 return nil, errors.Trace(err) 357 } 358 pools, err := a.poolManager.List() 359 if err != nil { 360 return nil, errors.Trace(err) 361 } 362 providers, err := a.registry.StorageProviderTypes() 363 if err != nil { 364 return nil, errors.Trace(err) 365 } 366 matches := buildFilter(filter) 367 results := append( 368 filterPools(pools, matches), 369 filterProviders(providers, matches)..., 370 ) 371 return results, nil 372 } 373 374 func buildFilter(filter params.StoragePoolFilter) func(n, p string) bool { 375 providerSet := set.NewStrings(filter.Providers...) 376 nameSet := set.NewStrings(filter.Names...) 377 378 matches := func(n, p string) bool { 379 // no filters supplied = pool matches criteria 380 if providerSet.IsEmpty() && nameSet.IsEmpty() { 381 return true 382 } 383 // if at least 1 name and type are supplied, use AND to match 384 if !providerSet.IsEmpty() && !nameSet.IsEmpty() { 385 return nameSet.Contains(n) && providerSet.Contains(p) 386 } 387 // Otherwise, if only names or types are supplied, use OR to match 388 return nameSet.Contains(n) || providerSet.Contains(p) 389 } 390 return matches 391 } 392 393 func filterProviders( 394 providers []storage.ProviderType, 395 matches func(n, p string) bool, 396 ) []params.StoragePool { 397 if len(providers) == 0 { 398 return nil 399 } 400 all := make([]params.StoragePool, 0, len(providers)) 401 for _, p := range providers { 402 ps := string(p) 403 if matches(ps, ps) { 404 all = append(all, params.StoragePool{Name: ps, Provider: ps}) 405 } 406 } 407 return all 408 } 409 410 func filterPools( 411 pools []*storage.Config, 412 matches func(n, p string) bool, 413 ) []params.StoragePool { 414 if len(pools) == 0 { 415 return nil 416 } 417 all := make([]params.StoragePool, 0, len(pools)) 418 for _, p := range pools { 419 if matches(p.Name(), string(p.Provider())) { 420 all = append(all, params.StoragePool{ 421 Name: p.Name(), 422 Provider: string(p.Provider()), 423 Attrs: p.Attrs(), 424 }) 425 } 426 } 427 return all 428 } 429 430 func (a *StorageAPI) validatePoolListFilter(filter params.StoragePoolFilter) error { 431 if err := a.validateProviderCriteria(filter.Providers); err != nil { 432 return errors.Trace(err) 433 } 434 if err := a.validateNameCriteria(filter.Names); err != nil { 435 return errors.Trace(err) 436 } 437 return nil 438 } 439 440 func (a *StorageAPI) validateNameCriteria(names []string) error { 441 for _, n := range names { 442 if !storage.IsValidPoolName(n) { 443 return errors.NotValidf("pool name %q", n) 444 } 445 } 446 return nil 447 } 448 449 func (a *StorageAPI) validateProviderCriteria(providers []string) error { 450 for _, p := range providers { 451 _, err := a.registry.StorageProvider(storage.ProviderType(p)) 452 if err != nil { 453 return errors.Trace(err) 454 } 455 } 456 return nil 457 } 458 459 // CreatePool creates a new pool with specified parameters. 460 func (a *StorageAPIv4) CreatePool(p params.StoragePool) error { 461 _, err := a.poolManager.Create( 462 p.Name, 463 storage.ProviderType(p.Provider), 464 p.Attrs) 465 return err 466 } 467 468 // CreatePool creates a new pool with specified parameters. 469 func (a *StorageAPI) CreatePool(p params.StoragePoolArgs) (params.ErrorResults, error) { 470 results := params.ErrorResults{ 471 Results: make([]params.ErrorResult, len(p.Pools)), 472 } 473 for i, pool := range p.Pools { 474 _, err := a.poolManager.Create( 475 pool.Name, 476 storage.ProviderType(pool.Provider), 477 pool.Attrs) 478 results.Results[i].Error = common.ServerError(err) 479 } 480 return results, nil 481 } 482 483 // ListVolumes lists volumes with the given filters. Each filter produces 484 // an independent list of volumes, or an error if the filter is invalid 485 // or the volumes could not be listed. 486 func (a *StorageAPI) ListVolumes(filters params.VolumeFilters) (params.VolumeDetailsListResults, error) { 487 if err := a.checkCanRead(); err != nil { 488 return params.VolumeDetailsListResults{}, errors.Trace(err) 489 } 490 results := params.VolumeDetailsListResults{ 491 Results: make([]params.VolumeDetailsListResult, len(filters.Filters)), 492 } 493 stVolumeAccess := a.storageAccess.VolumeAccess() 494 for i, filter := range filters.Filters { 495 volumes, volumeAttachments, err := filterVolumes(stVolumeAccess, filter) 496 if err != nil { 497 results.Results[i].Error = common.ServerError(err) 498 continue 499 } 500 details, err := createVolumeDetailsList( 501 a.backend, a.storageAccess, volumes, volumeAttachments, 502 ) 503 if err != nil { 504 results.Results[i].Error = common.ServerError(err) 505 continue 506 } 507 results.Results[i].Result = details 508 } 509 return results, nil 510 } 511 512 func filterVolumes( 513 stVolume storageVolume, 514 f params.VolumeFilter, 515 ) ([]state.Volume, map[names.VolumeTag][]state.VolumeAttachment, error) { 516 // Exit early if there's no volume support. 517 if stVolume == nil { 518 return nil, nil, nil 519 } 520 if f.IsEmpty() { 521 // No filter was specified: get all volumes, and all attachments. 522 volumes, err := stVolume.AllVolumes() 523 if err != nil { 524 return nil, nil, errors.Trace(err) 525 } 526 volumeAttachments := make(map[names.VolumeTag][]state.VolumeAttachment) 527 for _, v := range volumes { 528 attachments, err := stVolume.VolumeAttachments(v.VolumeTag()) 529 if err != nil { 530 return nil, nil, errors.Trace(err) 531 } 532 volumeAttachments[v.VolumeTag()] = attachments 533 } 534 return volumes, volumeAttachments, nil 535 } 536 volumesByTag := make(map[names.VolumeTag]state.Volume) 537 volumeAttachments := make(map[names.VolumeTag][]state.VolumeAttachment) 538 for _, machine := range f.Machines { 539 machineTag, err := names.ParseMachineTag(machine) 540 if err != nil { 541 return nil, nil, errors.Trace(err) 542 } 543 attachments, err := stVolume.MachineVolumeAttachments(machineTag) 544 if err != nil { 545 return nil, nil, errors.Trace(err) 546 } 547 for _, attachment := range attachments { 548 volumeTag := attachment.Volume() 549 volumesByTag[volumeTag] = nil 550 volumeAttachments[volumeTag] = append(volumeAttachments[volumeTag], attachment) 551 } 552 } 553 for volumeTag := range volumesByTag { 554 volume, err := stVolume.Volume(volumeTag) 555 if err != nil { 556 return nil, nil, errors.Trace(err) 557 } 558 volumesByTag[volumeTag] = volume 559 } 560 volumes := make([]state.Volume, 0, len(volumesByTag)) 561 for _, volume := range volumesByTag { 562 volumes = append(volumes, volume) 563 } 564 return volumes, volumeAttachments, nil 565 } 566 567 func createVolumeDetailsList( 568 backend backend, 569 st storageAccess, 570 volumes []state.Volume, 571 attachments map[names.VolumeTag][]state.VolumeAttachment, 572 ) ([]params.VolumeDetails, error) { 573 574 if len(volumes) == 0 { 575 return nil, nil 576 } 577 results := make([]params.VolumeDetails, len(volumes)) 578 for i, v := range volumes { 579 details, err := createVolumeDetails(backend, st, v, attachments[v.VolumeTag()]) 580 if err != nil { 581 return nil, errors.Annotatef( 582 err, "getting details for %s", 583 names.ReadableString(v.VolumeTag()), 584 ) 585 } 586 results[i] = *details 587 } 588 return results, nil 589 } 590 591 func createVolumeDetails( 592 backend backend, 593 st storageAccess, 594 v state.Volume, 595 attachments []state.VolumeAttachment, 596 ) (*params.VolumeDetails, error) { 597 598 details := ¶ms.VolumeDetails{ 599 VolumeTag: v.VolumeTag().String(), 600 Life: params.Life(v.Life().String()), 601 } 602 603 if info, err := v.Info(); err == nil { 604 details.Info = storagecommon.VolumeInfoFromState(info) 605 } 606 607 if len(attachments) > 0 { 608 details.MachineAttachments = make(map[string]params.VolumeAttachmentDetails, len(attachments)) 609 details.UnitAttachments = make(map[string]params.VolumeAttachmentDetails, len(attachments)) 610 for _, attachment := range attachments { 611 attDetails := params.VolumeAttachmentDetails{ 612 Life: params.Life(attachment.Life().String()), 613 } 614 if stateInfo, err := attachment.Info(); err == nil { 615 attDetails.VolumeAttachmentInfo = storagecommon.VolumeAttachmentInfoFromState( 616 stateInfo, 617 ) 618 } 619 if attachment.Host().Kind() == names.MachineTagKind { 620 details.MachineAttachments[attachment.Host().String()] = attDetails 621 } else { 622 details.UnitAttachments[attachment.Host().String()] = attDetails 623 } 624 } 625 } 626 627 status, err := v.Status() 628 if err != nil { 629 return nil, errors.Trace(err) 630 } 631 details.Status = common.EntityStatusFromState(status) 632 633 if storageTag, err := v.StorageInstance(); err == nil { 634 storageInstance, err := st.StorageInstance(storageTag) 635 if err != nil { 636 return nil, errors.Trace(err) 637 } 638 storageDetails, err := createStorageDetails(backend, st, storageInstance) 639 if err != nil { 640 return nil, errors.Trace(err) 641 } 642 details.Storage = storageDetails 643 } 644 645 return details, nil 646 } 647 648 // ListFilesystems returns a list of filesystems in the environment matching 649 // the provided filter. Each result describes a filesystem in detail, including 650 // the filesystem's attachments. 651 func (a *StorageAPI) ListFilesystems(filters params.FilesystemFilters) (params.FilesystemDetailsListResults, error) { 652 results := params.FilesystemDetailsListResults{ 653 Results: make([]params.FilesystemDetailsListResult, len(filters.Filters)), 654 } 655 if err := a.checkCanRead(); err != nil { 656 return results, errors.Trace(err) 657 } 658 659 stFileAccess := a.storageAccess.FilesystemAccess() 660 for i, filter := range filters.Filters { 661 filesystems, filesystemAttachments, err := filterFilesystems(stFileAccess, filter) 662 if err != nil { 663 results.Results[i].Error = common.ServerError(err) 664 continue 665 } 666 details, err := createFilesystemDetailsList( 667 a.backend, a.storageAccess, filesystems, filesystemAttachments, 668 ) 669 if err != nil { 670 results.Results[i].Error = common.ServerError(err) 671 continue 672 } 673 results.Results[i].Result = details 674 } 675 return results, nil 676 } 677 678 func filterFilesystems( 679 stFile storageFile, 680 f params.FilesystemFilter, 681 ) ([]state.Filesystem, map[names.FilesystemTag][]state.FilesystemAttachment, error) { 682 if f.IsEmpty() { 683 // No filter was specified: get all filesystems, and all attachments. 684 filesystems, err := stFile.AllFilesystems() 685 if err != nil { 686 return nil, nil, errors.Trace(err) 687 } 688 filesystemAttachments := make(map[names.FilesystemTag][]state.FilesystemAttachment) 689 for _, f := range filesystems { 690 attachments, err := stFile.FilesystemAttachments(f.FilesystemTag()) 691 if err != nil { 692 return nil, nil, errors.Trace(err) 693 } 694 filesystemAttachments[f.FilesystemTag()] = attachments 695 } 696 return filesystems, filesystemAttachments, nil 697 } 698 filesystemsByTag := make(map[names.FilesystemTag]state.Filesystem) 699 filesystemAttachments := make(map[names.FilesystemTag][]state.FilesystemAttachment) 700 for _, machine := range f.Machines { 701 machineTag, err := names.ParseMachineTag(machine) 702 if err != nil { 703 return nil, nil, errors.Trace(err) 704 } 705 attachments, err := stFile.MachineFilesystemAttachments(machineTag) 706 if err != nil { 707 return nil, nil, errors.Trace(err) 708 } 709 for _, attachment := range attachments { 710 filesystemTag := attachment.Filesystem() 711 filesystemsByTag[filesystemTag] = nil 712 filesystemAttachments[filesystemTag] = append(filesystemAttachments[filesystemTag], attachment) 713 } 714 } 715 for filesystemTag := range filesystemsByTag { 716 filesystem, err := stFile.Filesystem(filesystemTag) 717 if err != nil { 718 return nil, nil, errors.Trace(err) 719 } 720 filesystemsByTag[filesystemTag] = filesystem 721 } 722 filesystems := make([]state.Filesystem, 0, len(filesystemsByTag)) 723 for _, filesystem := range filesystemsByTag { 724 filesystems = append(filesystems, filesystem) 725 } 726 return filesystems, filesystemAttachments, nil 727 } 728 729 func createFilesystemDetailsList( 730 backend backend, 731 st storageAccess, 732 filesystems []state.Filesystem, 733 attachments map[names.FilesystemTag][]state.FilesystemAttachment, 734 ) ([]params.FilesystemDetails, error) { 735 736 if len(filesystems) == 0 { 737 return nil, nil 738 } 739 results := make([]params.FilesystemDetails, len(filesystems)) 740 for i, f := range filesystems { 741 details, err := createFilesystemDetails(backend, st, f, attachments[f.FilesystemTag()]) 742 if err != nil { 743 return nil, errors.Annotatef( 744 err, "getting details for %s", 745 names.ReadableString(f.FilesystemTag()), 746 ) 747 } 748 results[i] = *details 749 } 750 return results, nil 751 } 752 753 func createFilesystemDetails( 754 backend backend, 755 st storageAccess, 756 f state.Filesystem, 757 attachments []state.FilesystemAttachment, 758 ) (*params.FilesystemDetails, error) { 759 760 details := ¶ms.FilesystemDetails{ 761 FilesystemTag: f.FilesystemTag().String(), 762 Life: params.Life(f.Life().String()), 763 } 764 765 if volumeTag, err := f.Volume(); err == nil { 766 details.VolumeTag = volumeTag.String() 767 } 768 769 if info, err := f.Info(); err == nil { 770 details.Info = storagecommon.FilesystemInfoFromState(info) 771 } 772 773 if len(attachments) > 0 { 774 details.MachineAttachments = make(map[string]params.FilesystemAttachmentDetails, len(attachments)) 775 details.UnitAttachments = make(map[string]params.FilesystemAttachmentDetails, len(attachments)) 776 for _, attachment := range attachments { 777 attDetails := params.FilesystemAttachmentDetails{ 778 Life: params.Life(attachment.Life().String()), 779 } 780 if stateInfo, err := attachment.Info(); err == nil { 781 attDetails.FilesystemAttachmentInfo = storagecommon.FilesystemAttachmentInfoFromState( 782 stateInfo, 783 ) 784 } 785 if attachment.Host().Kind() == names.MachineTagKind { 786 details.MachineAttachments[attachment.Host().String()] = attDetails 787 } else { 788 details.UnitAttachments[attachment.Host().String()] = attDetails 789 } 790 } 791 } 792 793 status, err := f.Status() 794 if err != nil { 795 return nil, errors.Trace(err) 796 } 797 details.Status = common.EntityStatusFromState(status) 798 799 if storageTag, err := f.Storage(); err == nil { 800 storageInstance, err := st.StorageInstance(storageTag) 801 if err != nil { 802 return nil, errors.Trace(err) 803 } 804 storageDetails, err := createStorageDetails(backend, st, storageInstance) 805 if err != nil { 806 return nil, errors.Trace(err) 807 } 808 details.Storage = storageDetails 809 } 810 811 return details, nil 812 } 813 814 // AddToUnit validates and creates additional storage instances for units. 815 // A "CHANGE" block can block this operation. 816 func (a *StorageAPIv3) AddToUnit(args params.StoragesAddParams) (params.ErrorResults, error) { 817 v4results, err := a.addToUnit(args) 818 if err != nil { 819 return params.ErrorResults{}, err 820 } 821 v3results := make([]params.ErrorResult, len(v4results.Results)) 822 for i, result := range v4results.Results { 823 v3results[i].Error = result.Error 824 } 825 return params.ErrorResults{v3results}, nil 826 } 827 828 // AddToUnit validates and creates additional storage instances for units. 829 // A "CHANGE" block can block this operation. 830 func (a *StorageAPI) AddToUnit(args params.StoragesAddParams) (params.AddStorageResults, error) { 831 return a.addToUnit(args) 832 } 833 834 func (a *StorageAPI) addToUnit(args params.StoragesAddParams) (params.AddStorageResults, error) { 835 if err := a.checkCanWrite(); err != nil { 836 return params.AddStorageResults{}, errors.Trace(err) 837 } 838 839 // Check if changes are allowed and the operation may proceed. 840 blockChecker := common.NewBlockChecker(a.backend) 841 if err := blockChecker.ChangeAllowed(); err != nil { 842 return params.AddStorageResults{}, errors.Trace(err) 843 } 844 845 paramsToState := func(p params.StorageConstraints) state.StorageConstraints { 846 s := state.StorageConstraints{Pool: p.Pool} 847 if p.Size != nil { 848 s.Size = *p.Size 849 } 850 if p.Count != nil { 851 s.Count = *p.Count 852 } 853 return s 854 } 855 856 result := make([]params.AddStorageResult, len(args.Storages)) 857 for i, one := range args.Storages { 858 u, err := names.ParseUnitTag(one.UnitTag) 859 if err != nil { 860 result[i].Error = common.ServerError(err) 861 continue 862 } 863 864 tags, err := a.storageAccess.AddStorageForUnit( 865 u, one.StorageName, paramsToState(one.Constraints), 866 ) 867 if err != nil { 868 result[i].Error = common.ServerError(err) 869 } 870 tagStrings := make([]string, len(tags)) 871 for i, tag := range tags { 872 tagStrings[i] = tag.String() 873 } 874 result[i].Result = ¶ms.AddStorageDetails{ 875 StorageTags: tagStrings, 876 } 877 } 878 return params.AddStorageResults{Results: result}, nil 879 } 880 881 // Remove sets the specified storage entities to Dying, unless they are 882 // already Dying or Dead, such that the storage will eventually be removed 883 // from the model. If the arguments specify that the storage should be 884 // destroyed, then the associated cloud storage will be destroyed first; 885 // otherwise it will only be released from Juju's control. 886 func (a *StorageAPI) Remove(args params.RemoveStorage) (params.ErrorResults, error) { 887 return a.remove(args) 888 } 889 890 func (a *StorageAPI) remove(args params.RemoveStorage) (params.ErrorResults, error) { 891 if err := a.checkCanWrite(); err != nil { 892 return params.ErrorResults{}, errors.Trace(err) 893 } 894 895 blockChecker := common.NewBlockChecker(a.backend) 896 if err := blockChecker.RemoveAllowed(); err != nil { 897 return params.ErrorResults{}, errors.Trace(err) 898 } 899 900 result := make([]params.ErrorResult, len(args.Storage)) 901 for i, arg := range args.Storage { 902 tag, err := names.ParseStorageTag(arg.Tag) 903 if err != nil { 904 result[i].Error = common.ServerError(err) 905 continue 906 } 907 remove := a.storageAccess.DestroyStorageInstance 908 if !arg.DestroyStorage { 909 remove = a.storageAccess.ReleaseStorageInstance 910 } 911 result[i].Error = common.ServerError( 912 remove(tag, arg.DestroyAttachments), 913 ) 914 } 915 return params.ErrorResults{result}, nil 916 } 917 918 // Detach sets the specified storage attachments to Dying, unless they are 919 // already Dying or Dead. Any associated, persistent storage will remain 920 // alive. 921 func (a *StorageAPI) Detach(args params.StorageAttachmentIds) (params.ErrorResults, error) { 922 if err := a.checkCanWrite(); err != nil { 923 return params.ErrorResults{}, errors.Trace(err) 924 } 925 926 blockChecker := common.NewBlockChecker(a.backend) 927 if err := blockChecker.ChangeAllowed(); err != nil { 928 return params.ErrorResults{}, errors.Trace(err) 929 } 930 931 detachOne := func(arg params.StorageAttachmentId) error { 932 storageTag, err := names.ParseStorageTag(arg.StorageTag) 933 if err != nil { 934 return err 935 } 936 var unitTag names.UnitTag 937 if arg.UnitTag != "" { 938 var err error 939 unitTag, err = names.ParseUnitTag(arg.UnitTag) 940 if err != nil { 941 return err 942 } 943 } 944 return a.detachStorage(storageTag, unitTag) 945 } 946 947 result := make([]params.ErrorResult, len(args.Ids)) 948 for i, arg := range args.Ids { 949 result[i].Error = common.ServerError(detachOne(arg)) 950 } 951 return params.ErrorResults{result}, nil 952 } 953 954 func (a *StorageAPI) detachStorage(storageTag names.StorageTag, unitTag names.UnitTag) error { 955 if unitTag != (names.UnitTag{}) { 956 // The caller has specified a unit explicitly. Do 957 // not filter out "not found" errors in this case. 958 return a.storageAccess.DetachStorage(storageTag, unitTag) 959 } 960 attachments, err := a.storageAccess.StorageAttachments(storageTag) 961 if err != nil { 962 return errors.Trace(err) 963 } 964 if len(attachments) == 0 { 965 // No attachments: check if the storage exists at all. 966 if _, err := a.storageAccess.StorageInstance(storageTag); err != nil { 967 return errors.Trace(err) 968 } 969 } 970 for _, att := range attachments { 971 if att.Life() != state.Alive { 972 continue 973 } 974 err := a.storageAccess.DetachStorage(storageTag, att.Unit()) 975 if err != nil && !errors.IsNotFound(err) { 976 // We only care about NotFound errors if 977 // the user specified a unit explicitly. 978 return errors.Trace(err) 979 } 980 } 981 return nil 982 } 983 984 // Attach attaches existing storage instances to units. 985 // A "CHANGE" block can block this operation. 986 func (a *StorageAPI) Attach(args params.StorageAttachmentIds) (params.ErrorResults, error) { 987 if err := a.checkCanWrite(); err != nil { 988 return params.ErrorResults{}, errors.Trace(err) 989 } 990 991 blockChecker := common.NewBlockChecker(a.backend) 992 if err := blockChecker.ChangeAllowed(); err != nil { 993 return params.ErrorResults{}, errors.Trace(err) 994 } 995 996 attachOne := func(arg params.StorageAttachmentId) error { 997 storageTag, err := names.ParseStorageTag(arg.StorageTag) 998 if err != nil { 999 return err 1000 } 1001 unitTag, err := names.ParseUnitTag(arg.UnitTag) 1002 if err != nil { 1003 return err 1004 } 1005 return a.attachStorage(storageTag, unitTag) 1006 } 1007 1008 result := make([]params.ErrorResult, len(args.Ids)) 1009 for i, arg := range args.Ids { 1010 result[i].Error = common.ServerError(attachOne(arg)) 1011 } 1012 return params.ErrorResults{Results: result}, nil 1013 } 1014 1015 func (a *StorageAPI) attachStorage(storageTag names.StorageTag, unitTag names.UnitTag) error { 1016 return a.storageAccess.AttachStorage(storageTag, unitTag) 1017 } 1018 1019 // Import imports existing storage into the model. 1020 // A "CHANGE" block can block this operation. 1021 func (a *StorageAPI) Import(args params.BulkImportStorageParams) (params.ImportStorageResults, error) { 1022 if err := a.checkCanWrite(); err != nil { 1023 return params.ImportStorageResults{}, errors.Trace(err) 1024 } 1025 1026 blockChecker := common.NewBlockChecker(a.backend) 1027 if err := blockChecker.ChangeAllowed(); err != nil { 1028 return params.ImportStorageResults{}, errors.Trace(err) 1029 } 1030 1031 results := make([]params.ImportStorageResult, len(args.Storage)) 1032 for i, arg := range args.Storage { 1033 details, err := a.importStorage(arg) 1034 if err != nil { 1035 results[i].Error = common.ServerError(err) 1036 continue 1037 } 1038 results[i].Result = details 1039 } 1040 return params.ImportStorageResults{Results: results}, nil 1041 } 1042 1043 func (a *StorageAPI) importStorage(arg params.ImportStorageParams) (*params.ImportStorageDetails, error) { 1044 if arg.Kind != params.StorageKindFilesystem { 1045 // TODO(axw) implement support for volumes. 1046 return nil, errors.NotSupportedf("storage kind %q", arg.Kind.String()) 1047 } 1048 if !storage.IsValidPoolName(arg.Pool) { 1049 return nil, errors.NotValidf("pool name %q", arg.Pool) 1050 } 1051 1052 cfg, err := a.poolManager.Get(arg.Pool) 1053 if errors.IsNotFound(err) { 1054 cfg, err = storage.NewConfig( 1055 arg.Pool, 1056 storage.ProviderType(arg.Pool), 1057 map[string]interface{}{}, 1058 ) 1059 if err != nil { 1060 return nil, errors.Trace(err) 1061 } 1062 } else if err != nil { 1063 return nil, errors.Trace(err) 1064 } 1065 provider, err := a.registry.StorageProvider(cfg.Provider()) 1066 if err != nil { 1067 return nil, errors.Trace(err) 1068 } 1069 return a.importFilesystem(arg, provider, cfg) 1070 } 1071 1072 func (a *StorageAPI) importFilesystem( 1073 arg params.ImportStorageParams, 1074 provider storage.Provider, 1075 cfg *storage.Config, 1076 ) (*params.ImportStorageDetails, error) { 1077 resourceTags := map[string]string{ 1078 tags.JujuModel: a.backend.ModelTag().Id(), 1079 tags.JujuController: a.backend.ControllerTag().Id(), 1080 } 1081 var volumeInfo *state.VolumeInfo 1082 filesystemInfo := state.FilesystemInfo{Pool: arg.Pool} 1083 1084 // If the storage provider supports filesystems, import the filesystem, 1085 // otherwise import a volume which will back a filesystem. 1086 if provider.Supports(storage.StorageKindFilesystem) { 1087 filesystemSource, err := provider.FilesystemSource(cfg) 1088 if err != nil { 1089 return nil, errors.Trace(err) 1090 } 1091 filesystemImporter, ok := filesystemSource.(storage.FilesystemImporter) 1092 if !ok { 1093 return nil, errors.NotSupportedf( 1094 "importing filesystem with storage provider %q", 1095 cfg.Provider(), 1096 ) 1097 } 1098 info, err := filesystemImporter.ImportFilesystem(a.callContext, arg.ProviderId, resourceTags) 1099 if err != nil { 1100 return nil, errors.Annotate(err, "importing filesystem") 1101 } 1102 filesystemInfo.FilesystemId = arg.ProviderId 1103 filesystemInfo.Size = info.Size 1104 } else { 1105 volumeSource, err := provider.VolumeSource(cfg) 1106 if err != nil { 1107 return nil, errors.Trace(err) 1108 } 1109 volumeImporter, ok := volumeSource.(storage.VolumeImporter) 1110 if !ok { 1111 return nil, errors.NotSupportedf( 1112 "importing volume with storage provider %q", 1113 cfg.Provider(), 1114 ) 1115 } 1116 info, err := volumeImporter.ImportVolume(a.callContext, arg.ProviderId, resourceTags) 1117 if err != nil { 1118 return nil, errors.Annotate(err, "importing volume") 1119 } 1120 volumeInfo = &state.VolumeInfo{ 1121 HardwareId: info.HardwareId, 1122 WWN: info.WWN, 1123 Size: info.Size, 1124 Pool: arg.Pool, 1125 VolumeId: info.VolumeId, 1126 Persistent: info.Persistent, 1127 } 1128 filesystemInfo.Size = info.Size 1129 } 1130 1131 storageTag, err := a.storageAccess.FilesystemAccess().AddExistingFilesystem(filesystemInfo, volumeInfo, arg.StorageName) 1132 if err != nil { 1133 return nil, errors.Trace(err) 1134 } 1135 return ¶ms.ImportStorageDetails{ 1136 StorageTag: storageTag.String(), 1137 }, nil 1138 } 1139 1140 // RemovePool deletes the named pool 1141 func (a *StorageAPI) RemovePool(p params.StoragePoolDeleteArgs) (params.ErrorResults, error) { 1142 results := params.ErrorResults{ 1143 Results: make([]params.ErrorResult, len(p.Pools)), 1144 } 1145 if err := a.checkCanWrite(); err != nil { 1146 return results, errors.Trace(err) 1147 } 1148 for i, pool := range p.Pools { 1149 err := a.poolManager.Delete(pool.Name) 1150 if err != nil { 1151 results.Results[i].Error = common.ServerError(err) 1152 } 1153 1154 } 1155 return results, nil 1156 } 1157 1158 // UpdatePool deletes the named pool 1159 func (a *StorageAPI) UpdatePool(p params.StoragePoolArgs) (params.ErrorResults, error) { 1160 results := params.ErrorResults{ 1161 Results: make([]params.ErrorResult, len(p.Pools)), 1162 } 1163 if err := a.checkCanWrite(); err != nil { 1164 return results, errors.Trace(err) 1165 } 1166 for i, pool := range p.Pools { 1167 err := a.poolManager.Replace(pool.Name, pool.Provider, pool.Attrs) 1168 if err != nil { 1169 results.Results[i].Error = common.ServerError(err) 1170 } 1171 } 1172 return results, nil 1173 } 1174 1175 // Mask out old methods from the new API versions. The API reflection 1176 // code in rpc/rpcreflect/type.go:newMethod skips 2-argument methods, 1177 // so this removes the method as far as the RPC machinery is concerned. 1178 1179 // Added in current api version 1180 func (*StorageAPIv4) RemovePool(_, _ struct{}) {} 1181 func (*StorageAPIv4) UpdatePool(_, _ struct{}) {} 1182 1183 // Added in v4 1184 // Destroy was dropped in V4, replaced with Remove. 1185 func (*StorageAPIv3) Remove(_, _ struct{}) {} 1186 func (*StorageAPIv3) Import(_, _ struct{}) {} 1187 func (*StorageAPIv3) importStorage(_, _ struct{}) {} 1188 func (*StorageAPIv3) importFilesystem(_, _ struct{}) {} 1189 1190 // Destroy sets the specified storage entities to Dying, unless they are 1191 // already Dying or Dead. 1192 func (a *StorageAPIv3) Destroy(args params.Entities) (params.ErrorResults, error) { 1193 v4Args := params.RemoveStorage{ 1194 Storage: make([]params.RemoveStorageInstance, len(args.Entities)), 1195 } 1196 for i, arg := range args.Entities { 1197 v4Args.Storage[i] = params.RemoveStorageInstance{ 1198 Tag: arg.Tag, 1199 // The v3 behaviour was to detach the storage 1200 // at the same time as marking the storage Dying. 1201 DestroyAttachments: true, 1202 DestroyStorage: true, 1203 } 1204 } 1205 return a.remove(v4Args) 1206 }