github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/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/errors" 10 "github.com/juju/names" 11 "github.com/juju/utils/set" 12 13 "github.com/juju/juju/apiserver/common" 14 "github.com/juju/juju/apiserver/common/storagecommon" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/state" 17 "github.com/juju/juju/status" 18 "github.com/juju/juju/storage" 19 "github.com/juju/juju/storage/poolmanager" 20 "github.com/juju/juju/storage/provider/registry" 21 ) 22 23 func init() { 24 common.RegisterStandardFacade("Storage", 2, NewAPI) 25 } 26 27 // API implements the storage interface and is the concrete 28 // implementation of the api end point. 29 type API struct { 30 storage storageAccess 31 poolManager poolmanager.PoolManager 32 authorizer common.Authorizer 33 } 34 35 // createAPI returns a new storage API facade. 36 func createAPI( 37 st storageAccess, 38 pm poolmanager.PoolManager, 39 resources *common.Resources, 40 authorizer common.Authorizer, 41 ) (*API, error) { 42 if !authorizer.AuthClient() { 43 return nil, common.ErrPerm 44 } 45 46 return &API{ 47 storage: st, 48 poolManager: pm, 49 authorizer: authorizer, 50 }, nil 51 } 52 53 // NewAPI returns a new storage API facade. 54 func NewAPI( 55 st *state.State, 56 resources *common.Resources, 57 authorizer common.Authorizer, 58 ) (*API, error) { 59 return createAPI(getState(st), poolManager(st), resources, authorizer) 60 } 61 62 func poolManager(st *state.State) poolmanager.PoolManager { 63 return poolmanager.New(state.NewStateSettings(st)) 64 } 65 66 // StorageDetails retrieves and returns detailed information about desired 67 // storage identified by supplied tags. If specified storage cannot be 68 // retrieved, individual error is returned instead of storage information. 69 func (api *API) StorageDetails(entities params.Entities) (params.StorageDetailsResults, error) { 70 results := make([]params.StorageDetailsResult, len(entities.Entities)) 71 for i, entity := range entities.Entities { 72 storageTag, err := names.ParseStorageTag(entity.Tag) 73 if err != nil { 74 results[i].Error = common.ServerError(err) 75 continue 76 } 77 storageInstance, err := api.storage.StorageInstance(storageTag) 78 if err != nil { 79 results[i].Error = common.ServerError(err) 80 continue 81 } 82 details, err := createStorageDetails(api.storage, storageInstance) 83 if err != nil { 84 results[i].Error = common.ServerError(err) 85 continue 86 } 87 results[i].Result = details 88 } 89 return params.StorageDetailsResults{Results: results}, nil 90 } 91 92 // ListStorageDetails returns storage matching a filter. 93 func (api *API) ListStorageDetails(filters params.StorageFilters) (params.StorageDetailsListResults, error) { 94 results := params.StorageDetailsListResults{ 95 Results: make([]params.StorageDetailsListResult, len(filters.Filters)), 96 } 97 for i, filter := range filters.Filters { 98 list, err := api.listStorageDetails(filter) 99 if err != nil { 100 results.Results[i].Error = common.ServerError(err) 101 continue 102 } 103 results.Results[i].Result = list 104 } 105 return results, nil 106 } 107 108 func (api *API) listStorageDetails(filter params.StorageFilter) ([]params.StorageDetails, error) { 109 if filter != (params.StorageFilter{}) { 110 // StorageFilter has no fields at the time of writing, but 111 // check that no fields are set in case we forget to update 112 // this code. 113 return nil, errors.NotSupportedf("storage filters") 114 } 115 stateInstances, err := api.storage.AllStorageInstances() 116 if err != nil { 117 return nil, common.ServerError(err) 118 } 119 results := make([]params.StorageDetails, len(stateInstances)) 120 for i, stateInstance := range stateInstances { 121 details, err := createStorageDetails(api.storage, stateInstance) 122 if err != nil { 123 return nil, errors.Annotatef( 124 err, "getting details for %s", 125 names.ReadableString(stateInstance.Tag()), 126 ) 127 } 128 results[i] = *details 129 } 130 return results, nil 131 } 132 133 func createStorageDetails(st storageAccess, si state.StorageInstance) (*params.StorageDetails, error) { 134 // Get information from underlying volume or filesystem. 135 var persistent bool 136 var statusEntity status.StatusGetter 137 if si.Kind() != state.StorageKindBlock { 138 // TODO(axw) when we support persistent filesystems, 139 // e.g. CephFS, we'll need to do set "persistent" 140 // here too. 141 filesystem, err := st.StorageInstanceFilesystem(si.StorageTag()) 142 if err != nil { 143 return nil, errors.Trace(err) 144 } 145 statusEntity = filesystem 146 } else { 147 volume, err := st.StorageInstanceVolume(si.StorageTag()) 148 if err != nil { 149 return nil, errors.Trace(err) 150 } 151 if info, err := volume.Info(); err == nil { 152 persistent = info.Persistent 153 } 154 statusEntity = volume 155 } 156 status, err := statusEntity.Status() 157 if err != nil { 158 return nil, errors.Trace(err) 159 } 160 161 // Get unit storage attachments. 162 var storageAttachmentDetails map[string]params.StorageAttachmentDetails 163 storageAttachments, err := st.StorageAttachments(si.StorageTag()) 164 if err != nil { 165 return nil, errors.Trace(err) 166 } 167 if len(storageAttachments) > 0 { 168 storageAttachmentDetails = make(map[string]params.StorageAttachmentDetails) 169 for _, a := range storageAttachments { 170 machineTag, location, err := storageAttachmentInfo(st, a) 171 if err != nil { 172 return nil, errors.Trace(err) 173 } 174 details := params.StorageAttachmentDetails{ 175 a.StorageInstance().String(), 176 a.Unit().String(), 177 machineTag.String(), 178 location, 179 } 180 storageAttachmentDetails[a.Unit().String()] = details 181 } 182 } 183 184 return ¶ms.StorageDetails{ 185 StorageTag: si.Tag().String(), 186 OwnerTag: si.Owner().String(), 187 Kind: params.StorageKind(si.Kind()), 188 Status: common.EntityStatusFromState(status), 189 Persistent: persistent, 190 Attachments: storageAttachmentDetails, 191 }, nil 192 } 193 194 func storageAttachmentInfo(st storageAccess, a state.StorageAttachment) (_ names.MachineTag, location string, _ error) { 195 machineTag, err := st.UnitAssignedMachine(a.Unit()) 196 if errors.IsNotAssigned(err) { 197 return names.MachineTag{}, "", nil 198 } else if err != nil { 199 return names.MachineTag{}, "", errors.Trace(err) 200 } 201 info, err := storagecommon.StorageAttachmentInfo(st, a, machineTag) 202 if errors.IsNotProvisioned(err) { 203 return machineTag, "", nil 204 } else if err != nil { 205 return names.MachineTag{}, "", errors.Trace(err) 206 } 207 return machineTag, info.Location, nil 208 } 209 210 // ListPools returns a list of pools. 211 // If filter is provided, returned list only contains pools that match 212 // the filter. 213 // Pools can be filtered on names and provider types. 214 // If both names and types are provided as filter, 215 // pools that match either are returned. 216 // This method lists union of pools and environment provider types. 217 // If no filter is provided, all pools are returned. 218 func (a *API) ListPools( 219 filters params.StoragePoolFilters, 220 ) (params.StoragePoolsResults, error) { 221 results := params.StoragePoolsResults{ 222 Results: make([]params.StoragePoolsResult, len(filters.Filters)), 223 } 224 for i, filter := range filters.Filters { 225 pools, err := a.listPools(filter) 226 if err != nil { 227 results.Results[i].Error = common.ServerError(err) 228 continue 229 } 230 results.Results[i].Result = pools 231 } 232 return results, nil 233 } 234 235 func (a *API) listPools(filter params.StoragePoolFilter) ([]params.StoragePool, error) { 236 if err := a.validatePoolListFilter(filter); err != nil { 237 return nil, err 238 } 239 pools, err := a.poolManager.List() 240 if err != nil { 241 return nil, err 242 } 243 providers, err := a.allProviders() 244 if err != nil { 245 return nil, err 246 } 247 matches := buildFilter(filter) 248 results := append( 249 filterPools(pools, matches), 250 filterProviders(providers, matches)..., 251 ) 252 return results, nil 253 } 254 255 func buildFilter(filter params.StoragePoolFilter) func(n, p string) bool { 256 providerSet := set.NewStrings(filter.Providers...) 257 nameSet := set.NewStrings(filter.Names...) 258 259 matches := func(n, p string) bool { 260 // no filters supplied = pool matches criteria 261 if providerSet.IsEmpty() && nameSet.IsEmpty() { 262 return true 263 } 264 // if at least 1 name and type are supplied, use AND to match 265 if !providerSet.IsEmpty() && !nameSet.IsEmpty() { 266 return nameSet.Contains(n) && providerSet.Contains(string(p)) 267 } 268 // Otherwise, if only names or types are supplied, use OR to match 269 return nameSet.Contains(n) || providerSet.Contains(string(p)) 270 } 271 return matches 272 } 273 274 func filterProviders( 275 providers []storage.ProviderType, 276 matches func(n, p string) bool, 277 ) []params.StoragePool { 278 if len(providers) == 0 { 279 return nil 280 } 281 all := make([]params.StoragePool, 0, len(providers)) 282 for _, p := range providers { 283 ps := string(p) 284 if matches(ps, ps) { 285 all = append(all, params.StoragePool{Name: ps, Provider: ps}) 286 } 287 } 288 return all 289 } 290 291 func filterPools( 292 pools []*storage.Config, 293 matches func(n, p string) bool, 294 ) []params.StoragePool { 295 if len(pools) == 0 { 296 return nil 297 } 298 all := make([]params.StoragePool, 0, len(pools)) 299 for _, p := range pools { 300 if matches(p.Name(), string(p.Provider())) { 301 all = append(all, params.StoragePool{ 302 Name: p.Name(), 303 Provider: string(p.Provider()), 304 Attrs: p.Attrs(), 305 }) 306 } 307 } 308 return all 309 } 310 311 func (a *API) allProviders() ([]storage.ProviderType, error) { 312 envName, err := a.storage.ModelName() 313 if err != nil { 314 return nil, errors.Annotate(err, "getting env name") 315 } 316 if providers, ok := registry.EnvironStorageProviders(envName); ok { 317 return providers, nil 318 } 319 return nil, nil 320 } 321 322 func (a *API) validatePoolListFilter(filter params.StoragePoolFilter) error { 323 if err := a.validateProviderCriteria(filter.Providers); err != nil { 324 return errors.Trace(err) 325 } 326 if err := a.validateNameCriteria(filter.Names); err != nil { 327 return errors.Trace(err) 328 } 329 return nil 330 } 331 332 func (a *API) validateNameCriteria(names []string) error { 333 for _, n := range names { 334 if !storage.IsValidPoolName(n) { 335 return errors.NotValidf("pool name %q", n) 336 } 337 } 338 return nil 339 } 340 341 func (a *API) validateProviderCriteria(providers []string) error { 342 envName, err := a.storage.ModelName() 343 if err != nil { 344 return errors.Annotate(err, "getting model name") 345 } 346 for _, p := range providers { 347 if !registry.IsProviderSupported(envName, storage.ProviderType(p)) { 348 return errors.NotSupportedf("%q", p) 349 } 350 } 351 return nil 352 } 353 354 // CreatePool creates a new pool with specified parameters. 355 func (a *API) CreatePool(p params.StoragePool) error { 356 _, err := a.poolManager.Create( 357 p.Name, 358 storage.ProviderType(p.Provider), 359 p.Attrs) 360 return err 361 } 362 363 // ListVolumes lists volumes with the given filters. Each filter produces 364 // an independent list of volumes, or an error if the filter is invalid 365 // or the volumes could not be listed. 366 func (a *API) ListVolumes(filters params.VolumeFilters) (params.VolumeDetailsListResults, error) { 367 results := params.VolumeDetailsListResults{ 368 Results: make([]params.VolumeDetailsListResult, len(filters.Filters)), 369 } 370 for i, filter := range filters.Filters { 371 volumes, volumeAttachments, err := filterVolumes(a.storage, filter) 372 if err != nil { 373 results.Results[i].Error = common.ServerError(err) 374 continue 375 } 376 details, err := createVolumeDetailsList( 377 a.storage, volumes, volumeAttachments, 378 ) 379 if err != nil { 380 results.Results[i].Error = common.ServerError(err) 381 continue 382 } 383 results.Results[i].Result = details 384 } 385 return results, nil 386 } 387 388 func filterVolumes( 389 st storageAccess, 390 f params.VolumeFilter, 391 ) ([]state.Volume, map[names.VolumeTag][]state.VolumeAttachment, error) { 392 if f.IsEmpty() { 393 // No filter was specified: get all volumes, and all attachments. 394 volumes, err := st.AllVolumes() 395 if err != nil { 396 return nil, nil, errors.Trace(err) 397 } 398 volumeAttachments := make(map[names.VolumeTag][]state.VolumeAttachment) 399 for _, v := range volumes { 400 attachments, err := st.VolumeAttachments(v.VolumeTag()) 401 if err != nil { 402 return nil, nil, errors.Trace(err) 403 } 404 volumeAttachments[v.VolumeTag()] = attachments 405 } 406 return volumes, volumeAttachments, nil 407 } 408 volumesByTag := make(map[names.VolumeTag]state.Volume) 409 volumeAttachments := make(map[names.VolumeTag][]state.VolumeAttachment) 410 for _, machine := range f.Machines { 411 machineTag, err := names.ParseMachineTag(machine) 412 if err != nil { 413 return nil, nil, errors.Trace(err) 414 } 415 attachments, err := st.MachineVolumeAttachments(machineTag) 416 if err != nil { 417 return nil, nil, errors.Trace(err) 418 } 419 for _, attachment := range attachments { 420 volumeTag := attachment.Volume() 421 volumesByTag[volumeTag] = nil 422 volumeAttachments[volumeTag] = append(volumeAttachments[volumeTag], attachment) 423 } 424 } 425 for volumeTag := range volumesByTag { 426 volume, err := st.Volume(volumeTag) 427 if err != nil { 428 return nil, nil, errors.Trace(err) 429 } 430 volumesByTag[volumeTag] = volume 431 } 432 volumes := make([]state.Volume, 0, len(volumesByTag)) 433 for _, volume := range volumesByTag { 434 volumes = append(volumes, volume) 435 } 436 return volumes, volumeAttachments, nil 437 } 438 439 func createVolumeDetailsList( 440 st storageAccess, 441 volumes []state.Volume, 442 attachments map[names.VolumeTag][]state.VolumeAttachment, 443 ) ([]params.VolumeDetails, error) { 444 445 if len(volumes) == 0 { 446 return nil, nil 447 } 448 results := make([]params.VolumeDetails, len(volumes)) 449 for i, v := range volumes { 450 details, err := createVolumeDetails(st, v, attachments[v.VolumeTag()]) 451 if err != nil { 452 return nil, errors.Annotatef( 453 err, "getting details for %s", 454 names.ReadableString(v.VolumeTag()), 455 ) 456 } 457 results[i] = *details 458 } 459 return results, nil 460 } 461 462 func createVolumeDetails( 463 st storageAccess, v state.Volume, attachments []state.VolumeAttachment, 464 ) (*params.VolumeDetails, error) { 465 466 details := ¶ms.VolumeDetails{ 467 VolumeTag: v.VolumeTag().String(), 468 } 469 470 if info, err := v.Info(); err == nil { 471 details.Info = storagecommon.VolumeInfoFromState(info) 472 } 473 474 if len(attachments) > 0 { 475 details.MachineAttachments = make(map[string]params.VolumeAttachmentInfo, len(attachments)) 476 for _, attachment := range attachments { 477 stateInfo, err := attachment.Info() 478 var info params.VolumeAttachmentInfo 479 if err == nil { 480 info = storagecommon.VolumeAttachmentInfoFromState(stateInfo) 481 } 482 details.MachineAttachments[attachment.Machine().String()] = info 483 } 484 } 485 486 status, err := v.Status() 487 if err != nil { 488 return nil, errors.Trace(err) 489 } 490 details.Status = common.EntityStatusFromState(status) 491 492 if storageTag, err := v.StorageInstance(); err == nil { 493 storageInstance, err := st.StorageInstance(storageTag) 494 if err != nil { 495 return nil, errors.Trace(err) 496 } 497 storageDetails, err := createStorageDetails(st, storageInstance) 498 if err != nil { 499 return nil, errors.Trace(err) 500 } 501 details.Storage = storageDetails 502 } 503 504 return details, nil 505 } 506 507 // ListFilesystems returns a list of filesystems in the environment matching 508 // the provided filter. Each result describes a filesystem in detail, including 509 // the filesystem's attachments. 510 func (a *API) ListFilesystems(filters params.FilesystemFilters) (params.FilesystemDetailsListResults, error) { 511 results := params.FilesystemDetailsListResults{ 512 Results: make([]params.FilesystemDetailsListResult, len(filters.Filters)), 513 } 514 for i, filter := range filters.Filters { 515 filesystems, filesystemAttachments, err := filterFilesystems(a.storage, filter) 516 if err != nil { 517 results.Results[i].Error = common.ServerError(err) 518 continue 519 } 520 details, err := createFilesystemDetailsList( 521 a.storage, filesystems, filesystemAttachments, 522 ) 523 if err != nil { 524 results.Results[i].Error = common.ServerError(err) 525 continue 526 } 527 results.Results[i].Result = details 528 } 529 return results, nil 530 } 531 532 func filterFilesystems( 533 st storageAccess, 534 f params.FilesystemFilter, 535 ) ([]state.Filesystem, map[names.FilesystemTag][]state.FilesystemAttachment, error) { 536 if f.IsEmpty() { 537 // No filter was specified: get all filesystems, and all attachments. 538 filesystems, err := st.AllFilesystems() 539 if err != nil { 540 return nil, nil, errors.Trace(err) 541 } 542 filesystemAttachments := make(map[names.FilesystemTag][]state.FilesystemAttachment) 543 for _, f := range filesystems { 544 attachments, err := st.FilesystemAttachments(f.FilesystemTag()) 545 if err != nil { 546 return nil, nil, errors.Trace(err) 547 } 548 filesystemAttachments[f.FilesystemTag()] = attachments 549 } 550 return filesystems, filesystemAttachments, nil 551 } 552 filesystemsByTag := make(map[names.FilesystemTag]state.Filesystem) 553 filesystemAttachments := make(map[names.FilesystemTag][]state.FilesystemAttachment) 554 for _, machine := range f.Machines { 555 machineTag, err := names.ParseMachineTag(machine) 556 if err != nil { 557 return nil, nil, errors.Trace(err) 558 } 559 attachments, err := st.MachineFilesystemAttachments(machineTag) 560 if err != nil { 561 return nil, nil, errors.Trace(err) 562 } 563 for _, attachment := range attachments { 564 filesystemTag := attachment.Filesystem() 565 filesystemsByTag[filesystemTag] = nil 566 filesystemAttachments[filesystemTag] = append(filesystemAttachments[filesystemTag], attachment) 567 } 568 } 569 for filesystemTag := range filesystemsByTag { 570 filesystem, err := st.Filesystem(filesystemTag) 571 if err != nil { 572 return nil, nil, errors.Trace(err) 573 } 574 filesystemsByTag[filesystemTag] = filesystem 575 } 576 filesystems := make([]state.Filesystem, 0, len(filesystemsByTag)) 577 for _, filesystem := range filesystemsByTag { 578 filesystems = append(filesystems, filesystem) 579 } 580 return filesystems, filesystemAttachments, nil 581 } 582 583 func createFilesystemDetailsList( 584 st storageAccess, 585 filesystems []state.Filesystem, 586 attachments map[names.FilesystemTag][]state.FilesystemAttachment, 587 ) ([]params.FilesystemDetails, error) { 588 589 if len(filesystems) == 0 { 590 return nil, nil 591 } 592 results := make([]params.FilesystemDetails, len(filesystems)) 593 for i, f := range filesystems { 594 details, err := createFilesystemDetails(st, f, attachments[f.FilesystemTag()]) 595 if err != nil { 596 return nil, errors.Annotatef( 597 err, "getting details for %s", 598 names.ReadableString(f.FilesystemTag()), 599 ) 600 } 601 results[i] = *details 602 } 603 return results, nil 604 } 605 606 func createFilesystemDetails( 607 st storageAccess, f state.Filesystem, attachments []state.FilesystemAttachment, 608 ) (*params.FilesystemDetails, error) { 609 610 details := ¶ms.FilesystemDetails{ 611 FilesystemTag: f.FilesystemTag().String(), 612 } 613 614 if volumeTag, err := f.Volume(); err == nil { 615 details.VolumeTag = volumeTag.String() 616 } 617 618 if info, err := f.Info(); err == nil { 619 details.Info = storagecommon.FilesystemInfoFromState(info) 620 } 621 622 if len(attachments) > 0 { 623 details.MachineAttachments = make(map[string]params.FilesystemAttachmentInfo, len(attachments)) 624 for _, attachment := range attachments { 625 stateInfo, err := attachment.Info() 626 var info params.FilesystemAttachmentInfo 627 if err == nil { 628 info = storagecommon.FilesystemAttachmentInfoFromState(stateInfo) 629 } 630 details.MachineAttachments[attachment.Machine().String()] = info 631 } 632 } 633 634 status, err := f.Status() 635 if err != nil { 636 return nil, errors.Trace(err) 637 } 638 details.Status = common.EntityStatusFromState(status) 639 640 if storageTag, err := f.Storage(); err == nil { 641 storageInstance, err := st.StorageInstance(storageTag) 642 if err != nil { 643 return nil, errors.Trace(err) 644 } 645 storageDetails, err := createStorageDetails(st, storageInstance) 646 if err != nil { 647 return nil, errors.Trace(err) 648 } 649 details.Storage = storageDetails 650 } 651 652 return details, nil 653 } 654 655 // AddToUnit validates and creates additional storage instances for units. 656 // This method handles bulk add operations and 657 // a failure on one individual storage instance does not block remaining 658 // instances from being processed. 659 // A "CHANGE" block can block this operation. 660 func (a *API) AddToUnit(args params.StoragesAddParams) (params.ErrorResults, error) { 661 // Check if changes are allowed and the operation may proceed. 662 blockChecker := common.NewBlockChecker(a.storage) 663 if err := blockChecker.ChangeAllowed(); err != nil { 664 return params.ErrorResults{}, errors.Trace(err) 665 } 666 667 if len(args.Storages) == 0 { 668 return params.ErrorResults{}, nil 669 } 670 671 serverErr := func(err error) params.ErrorResult { 672 if errors.IsNotFound(err) { 673 err = common.ErrPerm 674 } 675 return params.ErrorResult{Error: common.ServerError(err)} 676 } 677 678 paramsToState := func(p params.StorageConstraints) state.StorageConstraints { 679 s := state.StorageConstraints{Pool: p.Pool} 680 if p.Size != nil { 681 s.Size = *p.Size 682 } 683 if p.Count != nil { 684 s.Count = *p.Count 685 } 686 return s 687 } 688 689 result := make([]params.ErrorResult, len(args.Storages)) 690 for i, one := range args.Storages { 691 u, err := names.ParseUnitTag(one.UnitTag) 692 if err != nil { 693 result[i] = serverErr( 694 errors.Annotatef(err, "parsing unit tag %v", one.UnitTag)) 695 continue 696 } 697 698 err = a.storage.AddStorageForUnit(u, 699 one.StorageName, 700 paramsToState(one.Constraints)) 701 if err != nil { 702 result[i] = serverErr( 703 errors.Annotatef(err, "adding storage %v for %v", one.StorageName, one.UnitTag)) 704 } 705 } 706 return params.ErrorResults{Results: result}, nil 707 }