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