github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/storageprovisioner/storageprovisioner.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/common/storagecommon" 13 "github.com/juju/juju/apiserver/facade" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/state" 16 "github.com/juju/juju/state/watcher" 17 "github.com/juju/juju/storage" 18 "github.com/juju/juju/storage/poolmanager" 19 ) 20 21 var logger = loggo.GetLogger("juju.apiserver.storageprovisioner") 22 23 // StorageProvisionerAPI provides access to the Provisioner API facade. 24 type StorageProvisionerAPI struct { 25 *common.LifeGetter 26 *common.DeadEnsurer 27 *common.InstanceIdGetter 28 *common.StatusSetter 29 30 st Backend 31 resources facade.Resources 32 authorizer facade.Authorizer 33 registry storage.ProviderRegistry 34 poolManager poolmanager.PoolManager 35 getScopeAuthFunc common.GetAuthFunc 36 getStorageEntityAuthFunc common.GetAuthFunc 37 getMachineAuthFunc common.GetAuthFunc 38 getBlockDevicesAuthFunc common.GetAuthFunc 39 getAttachmentAuthFunc func() (func(names.MachineTag, names.Tag) bool, error) 40 } 41 42 // NewStorageProvisionerAPI creates a new server-side StorageProvisionerAPI facade. 43 func NewStorageProvisionerAPI( 44 st Backend, 45 resources facade.Resources, 46 authorizer facade.Authorizer, 47 registry storage.ProviderRegistry, 48 poolManager poolmanager.PoolManager, 49 ) (*StorageProvisionerAPI, error) { 50 if !authorizer.AuthMachineAgent() { 51 return nil, common.ErrPerm 52 } 53 canAccessStorageMachine := func(tag names.MachineTag, allowEnvironManager bool) bool { 54 authEntityTag := authorizer.GetAuthTag() 55 if tag == authEntityTag { 56 // Machine agents can access volumes 57 // scoped to their own machine. 58 return true 59 } 60 parentId := state.ParentId(tag.Id()) 61 if parentId == "" { 62 return allowEnvironManager && authorizer.AuthModelManager() 63 } 64 // All containers with the authenticated 65 // machine as a parent are accessible by it. 66 return names.NewMachineTag(parentId) == authEntityTag 67 } 68 getScopeAuthFunc := func() (common.AuthFunc, error) { 69 return func(tag names.Tag) bool { 70 switch tag := tag.(type) { 71 case names.ModelTag: 72 // Environment managers can access all volumes 73 // and filesystems scoped to the environment. 74 isModelManager := authorizer.AuthModelManager() 75 return isModelManager && tag == st.ModelTag() 76 case names.MachineTag: 77 return canAccessStorageMachine(tag, false) 78 default: 79 return false 80 } 81 }, nil 82 } 83 canAccessStorageEntity := func(tag names.Tag, allowMachines bool) bool { 84 switch tag := tag.(type) { 85 case names.VolumeTag: 86 machineTag, ok := names.VolumeMachine(tag) 87 if ok { 88 return canAccessStorageMachine(machineTag, false) 89 } 90 return authorizer.AuthModelManager() 91 case names.FilesystemTag: 92 machineTag, ok := names.FilesystemMachine(tag) 93 if ok { 94 return canAccessStorageMachine(machineTag, false) 95 } 96 return authorizer.AuthModelManager() 97 case names.MachineTag: 98 return allowMachines && canAccessStorageMachine(tag, true) 99 default: 100 return false 101 } 102 } 103 getStorageEntityAuthFunc := func() (common.AuthFunc, error) { 104 return func(tag names.Tag) bool { 105 return canAccessStorageEntity(tag, false) 106 }, nil 107 } 108 getLifeAuthFunc := func() (common.AuthFunc, error) { 109 return func(tag names.Tag) bool { 110 return canAccessStorageEntity(tag, true) 111 }, nil 112 } 113 getAttachmentAuthFunc := func() (func(names.MachineTag, names.Tag) bool, error) { 114 // getAttachmentAuthFunc returns a function that validates 115 // access by the authenticated user to an attachment. 116 return func(machineTag names.MachineTag, attachmentTag names.Tag) bool { 117 // Machine agents can access their own machine, and 118 // machines contained. Environment managers can access 119 // top-level machines. 120 if !canAccessStorageMachine(machineTag, true) { 121 return false 122 } 123 // Environment managers can access model-scoped 124 // volumes and volumes scoped to their own machines. 125 // Other machine agents can access volumes regardless 126 // of their scope. 127 if !authorizer.AuthModelManager() { 128 return true 129 } 130 var machineScope names.MachineTag 131 var hasMachineScope bool 132 switch attachmentTag := attachmentTag.(type) { 133 case names.VolumeTag: 134 machineScope, hasMachineScope = names.VolumeMachine(attachmentTag) 135 case names.FilesystemTag: 136 machineScope, hasMachineScope = names.FilesystemMachine(attachmentTag) 137 } 138 return !hasMachineScope || machineScope == authorizer.GetAuthTag() 139 }, nil 140 } 141 getMachineAuthFunc := func() (common.AuthFunc, error) { 142 return func(tag names.Tag) bool { 143 if tag, ok := tag.(names.MachineTag); ok { 144 return canAccessStorageMachine(tag, true) 145 } 146 return false 147 }, nil 148 } 149 getBlockDevicesAuthFunc := func() (common.AuthFunc, error) { 150 return func(tag names.Tag) bool { 151 if tag, ok := tag.(names.MachineTag); ok { 152 return canAccessStorageMachine(tag, false) 153 } 154 return false 155 }, nil 156 } 157 return &StorageProvisionerAPI{ 158 LifeGetter: common.NewLifeGetter(st, getLifeAuthFunc), 159 DeadEnsurer: common.NewDeadEnsurer(st, getStorageEntityAuthFunc), 160 InstanceIdGetter: common.NewInstanceIdGetter(st, getMachineAuthFunc), 161 StatusSetter: common.NewStatusSetter(st, getStorageEntityAuthFunc), 162 163 st: st, 164 resources: resources, 165 authorizer: authorizer, 166 registry: registry, 167 poolManager: poolManager, 168 getScopeAuthFunc: getScopeAuthFunc, 169 getStorageEntityAuthFunc: getStorageEntityAuthFunc, 170 getAttachmentAuthFunc: getAttachmentAuthFunc, 171 getMachineAuthFunc: getMachineAuthFunc, 172 getBlockDevicesAuthFunc: getBlockDevicesAuthFunc, 173 }, nil 174 } 175 176 // WatchBlockDevices watches for changes to the specified machines' block devices. 177 func (s *StorageProvisionerAPI) WatchBlockDevices(args params.Entities) (params.NotifyWatchResults, error) { 178 canAccess, err := s.getBlockDevicesAuthFunc() 179 if err != nil { 180 return params.NotifyWatchResults{}, common.ServerError(common.ErrPerm) 181 } 182 results := params.NotifyWatchResults{ 183 Results: make([]params.NotifyWatchResult, len(args.Entities)), 184 } 185 one := func(arg params.Entity) (string, error) { 186 machineTag, err := names.ParseMachineTag(arg.Tag) 187 if err != nil { 188 return "", err 189 } 190 if !canAccess(machineTag) { 191 return "", common.ErrPerm 192 } 193 w := s.st.WatchBlockDevices(machineTag) 194 if _, ok := <-w.Changes(); ok { 195 return s.resources.Register(w), nil 196 } 197 return "", watcher.EnsureErr(w) 198 } 199 for i, arg := range args.Entities { 200 var result params.NotifyWatchResult 201 id, err := one(arg) 202 if err != nil { 203 result.Error = common.ServerError(err) 204 } else { 205 result.NotifyWatcherId = id 206 } 207 results.Results[i] = result 208 } 209 return results, nil 210 } 211 212 // WatchMachines watches for changes to the specified machines. 213 func (s *StorageProvisionerAPI) WatchMachines(args params.Entities) (params.NotifyWatchResults, error) { 214 canAccess, err := s.getMachineAuthFunc() 215 if err != nil { 216 return params.NotifyWatchResults{}, common.ServerError(common.ErrPerm) 217 } 218 results := params.NotifyWatchResults{ 219 Results: make([]params.NotifyWatchResult, len(args.Entities)), 220 } 221 one := func(arg params.Entity) (string, error) { 222 machineTag, err := names.ParseMachineTag(arg.Tag) 223 if err != nil { 224 return "", err 225 } 226 if !canAccess(machineTag) { 227 return "", common.ErrPerm 228 } 229 w, err := s.st.WatchMachine(machineTag) 230 if err != nil { 231 return "", errors.Trace(err) 232 } 233 if _, ok := <-w.Changes(); ok { 234 return s.resources.Register(w), nil 235 } 236 return "", watcher.EnsureErr(w) 237 } 238 for i, arg := range args.Entities { 239 var result params.NotifyWatchResult 240 id, err := one(arg) 241 if err != nil { 242 result.Error = common.ServerError(err) 243 } else { 244 result.NotifyWatcherId = id 245 } 246 results.Results[i] = result 247 } 248 return results, nil 249 } 250 251 // WatchVolumes watches for changes to volumes scoped to the 252 // entity with the tag passed to NewState. 253 func (s *StorageProvisionerAPI) WatchVolumes(args params.Entities) (params.StringsWatchResults, error) { 254 return s.watchStorageEntities(args, s.st.WatchModelVolumes, s.st.WatchMachineVolumes) 255 } 256 257 // WatchFilesystems watches for changes to filesystems scoped 258 // to the entity with the tag passed to NewState. 259 func (s *StorageProvisionerAPI) WatchFilesystems(args params.Entities) (params.StringsWatchResults, error) { 260 return s.watchStorageEntities(args, s.st.WatchModelFilesystems, s.st.WatchMachineFilesystems) 261 } 262 263 func (s *StorageProvisionerAPI) watchStorageEntities( 264 args params.Entities, 265 watchEnvironStorage func() state.StringsWatcher, 266 watchMachineStorage func(names.MachineTag) state.StringsWatcher, 267 ) (params.StringsWatchResults, error) { 268 canAccess, err := s.getScopeAuthFunc() 269 if err != nil { 270 return params.StringsWatchResults{}, common.ServerError(common.ErrPerm) 271 } 272 results := params.StringsWatchResults{ 273 Results: make([]params.StringsWatchResult, len(args.Entities)), 274 } 275 one := func(arg params.Entity) (string, []string, error) { 276 tag, err := names.ParseTag(arg.Tag) 277 if err != nil || !canAccess(tag) { 278 return "", nil, common.ErrPerm 279 } 280 var w state.StringsWatcher 281 if tag, ok := tag.(names.MachineTag); ok { 282 w = watchMachineStorage(tag) 283 } else { 284 w = watchEnvironStorage() 285 } 286 if changes, ok := <-w.Changes(); ok { 287 return s.resources.Register(w), changes, nil 288 } 289 return "", nil, watcher.EnsureErr(w) 290 } 291 for i, arg := range args.Entities { 292 var result params.StringsWatchResult 293 id, changes, err := one(arg) 294 if err != nil { 295 result.Error = common.ServerError(err) 296 } else { 297 result.StringsWatcherId = id 298 result.Changes = changes 299 } 300 results.Results[i] = result 301 } 302 return results, nil 303 } 304 305 // WatchVolumeAttachments watches for changes to volume attachments scoped to 306 // the entity with the tag passed to NewState. 307 func (s *StorageProvisionerAPI) WatchVolumeAttachments(args params.Entities) (params.MachineStorageIdsWatchResults, error) { 308 return s.watchAttachments( 309 args, 310 s.st.WatchEnvironVolumeAttachments, 311 s.st.WatchMachineVolumeAttachments, 312 storagecommon.ParseVolumeAttachmentIds, 313 ) 314 } 315 316 // WatchFilesystemAttachments watches for changes to filesystem attachments 317 // scoped to the entity with the tag passed to NewState. 318 func (s *StorageProvisionerAPI) WatchFilesystemAttachments(args params.Entities) (params.MachineStorageIdsWatchResults, error) { 319 return s.watchAttachments( 320 args, 321 s.st.WatchEnvironFilesystemAttachments, 322 s.st.WatchMachineFilesystemAttachments, 323 storagecommon.ParseFilesystemAttachmentIds, 324 ) 325 } 326 327 func (s *StorageProvisionerAPI) watchAttachments( 328 args params.Entities, 329 watchEnvironAttachments func() state.StringsWatcher, 330 watchMachineAttachments func(names.MachineTag) state.StringsWatcher, 331 parseAttachmentIds func([]string) ([]params.MachineStorageId, error), 332 ) (params.MachineStorageIdsWatchResults, error) { 333 canAccess, err := s.getScopeAuthFunc() 334 if err != nil { 335 return params.MachineStorageIdsWatchResults{}, common.ServerError(common.ErrPerm) 336 } 337 results := params.MachineStorageIdsWatchResults{ 338 Results: make([]params.MachineStorageIdsWatchResult, len(args.Entities)), 339 } 340 one := func(arg params.Entity) (string, []params.MachineStorageId, error) { 341 tag, err := names.ParseTag(arg.Tag) 342 if err != nil || !canAccess(tag) { 343 return "", nil, common.ErrPerm 344 } 345 var w state.StringsWatcher 346 if tag, ok := tag.(names.MachineTag); ok { 347 w = watchMachineAttachments(tag) 348 } else { 349 w = watchEnvironAttachments() 350 } 351 if stringChanges, ok := <-w.Changes(); ok { 352 changes, err := parseAttachmentIds(stringChanges) 353 if err != nil { 354 w.Stop() 355 return "", nil, err 356 } 357 return s.resources.Register(w), changes, nil 358 } 359 return "", nil, watcher.EnsureErr(w) 360 } 361 for i, arg := range args.Entities { 362 var result params.MachineStorageIdsWatchResult 363 id, changes, err := one(arg) 364 if err != nil { 365 result.Error = common.ServerError(err) 366 } else { 367 result.MachineStorageIdsWatcherId = id 368 result.Changes = changes 369 } 370 results.Results[i] = result 371 } 372 return results, nil 373 } 374 375 // Volumes returns details of volumes with the specified tags. 376 func (s *StorageProvisionerAPI) Volumes(args params.Entities) (params.VolumeResults, error) { 377 canAccess, err := s.getStorageEntityAuthFunc() 378 if err != nil { 379 return params.VolumeResults{}, common.ServerError(common.ErrPerm) 380 } 381 results := params.VolumeResults{ 382 Results: make([]params.VolumeResult, len(args.Entities)), 383 } 384 one := func(arg params.Entity) (params.Volume, error) { 385 tag, err := names.ParseVolumeTag(arg.Tag) 386 if err != nil || !canAccess(tag) { 387 return params.Volume{}, common.ErrPerm 388 } 389 volume, err := s.st.Volume(tag) 390 if errors.IsNotFound(err) { 391 return params.Volume{}, common.ErrPerm 392 } else if err != nil { 393 return params.Volume{}, err 394 } 395 return storagecommon.VolumeFromState(volume) 396 } 397 for i, arg := range args.Entities { 398 var result params.VolumeResult 399 volume, err := one(arg) 400 if err != nil { 401 result.Error = common.ServerError(err) 402 } else { 403 result.Result = volume 404 } 405 results.Results[i] = result 406 } 407 return results, nil 408 } 409 410 // Filesystems returns details of filesystems with the specified tags. 411 func (s *StorageProvisionerAPI) Filesystems(args params.Entities) (params.FilesystemResults, error) { 412 canAccess, err := s.getStorageEntityAuthFunc() 413 if err != nil { 414 return params.FilesystemResults{}, common.ServerError(common.ErrPerm) 415 } 416 results := params.FilesystemResults{ 417 Results: make([]params.FilesystemResult, len(args.Entities)), 418 } 419 one := func(arg params.Entity) (params.Filesystem, error) { 420 tag, err := names.ParseFilesystemTag(arg.Tag) 421 if err != nil || !canAccess(tag) { 422 return params.Filesystem{}, common.ErrPerm 423 } 424 filesystem, err := s.st.Filesystem(tag) 425 if errors.IsNotFound(err) { 426 return params.Filesystem{}, common.ErrPerm 427 } else if err != nil { 428 return params.Filesystem{}, err 429 } 430 return storagecommon.FilesystemFromState(filesystem) 431 } 432 for i, arg := range args.Entities { 433 var result params.FilesystemResult 434 filesystem, err := one(arg) 435 if err != nil { 436 result.Error = common.ServerError(err) 437 } else { 438 result.Result = filesystem 439 } 440 results.Results[i] = result 441 } 442 return results, nil 443 } 444 445 // VolumeAttachments returns details of volume attachments with the specified IDs. 446 func (s *StorageProvisionerAPI) VolumeAttachments(args params.MachineStorageIds) (params.VolumeAttachmentResults, error) { 447 canAccess, err := s.getAttachmentAuthFunc() 448 if err != nil { 449 return params.VolumeAttachmentResults{}, common.ServerError(common.ErrPerm) 450 } 451 results := params.VolumeAttachmentResults{ 452 Results: make([]params.VolumeAttachmentResult, len(args.Ids)), 453 } 454 one := func(arg params.MachineStorageId) (params.VolumeAttachment, error) { 455 volumeAttachment, err := s.oneVolumeAttachment(arg, canAccess) 456 if err != nil { 457 return params.VolumeAttachment{}, err 458 } 459 return storagecommon.VolumeAttachmentFromState(volumeAttachment) 460 } 461 for i, arg := range args.Ids { 462 var result params.VolumeAttachmentResult 463 volumeAttachment, err := one(arg) 464 if err != nil { 465 result.Error = common.ServerError(err) 466 } else { 467 result.Result = volumeAttachment 468 } 469 results.Results[i] = result 470 } 471 return results, nil 472 } 473 474 // VolumeBlockDevices returns details of the block devices corresponding to the 475 // volume attachments with the specified IDs. 476 func (s *StorageProvisionerAPI) VolumeBlockDevices(args params.MachineStorageIds) (params.BlockDeviceResults, error) { 477 canAccess, err := s.getAttachmentAuthFunc() 478 if err != nil { 479 return params.BlockDeviceResults{}, common.ServerError(common.ErrPerm) 480 } 481 results := params.BlockDeviceResults{ 482 Results: make([]params.BlockDeviceResult, len(args.Ids)), 483 } 484 one := func(arg params.MachineStorageId) (storage.BlockDevice, error) { 485 stateBlockDevice, err := s.oneVolumeBlockDevice(arg, canAccess) 486 if err != nil { 487 return storage.BlockDevice{}, err 488 } 489 return storagecommon.BlockDeviceFromState(stateBlockDevice), nil 490 } 491 for i, arg := range args.Ids { 492 var result params.BlockDeviceResult 493 blockDevice, err := one(arg) 494 if err != nil { 495 result.Error = common.ServerError(err) 496 } else { 497 result.Result = blockDevice 498 } 499 results.Results[i] = result 500 } 501 return results, nil 502 } 503 504 // FilesystemAttachments returns details of filesystem attachments with the specified IDs. 505 func (s *StorageProvisionerAPI) FilesystemAttachments(args params.MachineStorageIds) (params.FilesystemAttachmentResults, error) { 506 canAccess, err := s.getAttachmentAuthFunc() 507 if err != nil { 508 return params.FilesystemAttachmentResults{}, common.ServerError(common.ErrPerm) 509 } 510 results := params.FilesystemAttachmentResults{ 511 Results: make([]params.FilesystemAttachmentResult, len(args.Ids)), 512 } 513 one := func(arg params.MachineStorageId) (params.FilesystemAttachment, error) { 514 filesystemAttachment, err := s.oneFilesystemAttachment(arg, canAccess) 515 if err != nil { 516 return params.FilesystemAttachment{}, err 517 } 518 return storagecommon.FilesystemAttachmentFromState(filesystemAttachment) 519 } 520 for i, arg := range args.Ids { 521 var result params.FilesystemAttachmentResult 522 filesystemAttachment, err := one(arg) 523 if err != nil { 524 result.Error = common.ServerError(err) 525 } else { 526 result.Result = filesystemAttachment 527 } 528 results.Results[i] = result 529 } 530 return results, nil 531 } 532 533 // VolumeParams returns the parameters for creating or destroying 534 // the volumes with the specified tags. 535 func (s *StorageProvisionerAPI) VolumeParams(args params.Entities) (params.VolumeParamsResults, error) { 536 canAccess, err := s.getStorageEntityAuthFunc() 537 if err != nil { 538 return params.VolumeParamsResults{}, err 539 } 540 modelCfg, err := s.st.ModelConfig() 541 if err != nil { 542 return params.VolumeParamsResults{}, err 543 } 544 controllerCfg, err := s.st.ControllerConfig() 545 if err != nil { 546 return params.VolumeParamsResults{}, err 547 } 548 results := params.VolumeParamsResults{ 549 Results: make([]params.VolumeParamsResult, len(args.Entities)), 550 } 551 one := func(arg params.Entity) (params.VolumeParams, error) { 552 tag, err := names.ParseVolumeTag(arg.Tag) 553 if err != nil || !canAccess(tag) { 554 return params.VolumeParams{}, common.ErrPerm 555 } 556 volume, err := s.st.Volume(tag) 557 if errors.IsNotFound(err) { 558 return params.VolumeParams{}, common.ErrPerm 559 } else if err != nil { 560 return params.VolumeParams{}, err 561 } 562 volumeAttachments, err := s.st.VolumeAttachments(tag) 563 if err != nil { 564 return params.VolumeParams{}, err 565 } 566 storageInstance, err := storagecommon.MaybeAssignedStorageInstance( 567 volume.StorageInstance, 568 s.st.StorageInstance, 569 ) 570 if err != nil { 571 return params.VolumeParams{}, err 572 } 573 volumeParams, err := storagecommon.VolumeParams( 574 volume, storageInstance, modelCfg.UUID(), controllerCfg.ControllerUUID(), 575 modelCfg, s.poolManager, s.registry, 576 ) 577 if err != nil { 578 return params.VolumeParams{}, err 579 } 580 if len(volumeAttachments) == 1 { 581 // There is exactly one attachment to be made, so make 582 // it immediately. Otherwise we will defer attachments 583 // until later. 584 volumeAttachment := volumeAttachments[0] 585 volumeAttachmentParams, ok := volumeAttachment.Params() 586 if !ok { 587 return params.VolumeParams{}, errors.Errorf( 588 "volume %q is already attached to machine %q", 589 volumeAttachment.Volume().Id(), 590 volumeAttachment.Machine().Id(), 591 ) 592 } 593 machineTag := volumeAttachment.Machine() 594 instanceId, err := s.st.MachineInstanceId(machineTag) 595 if errors.IsNotProvisioned(err) { 596 // Leave the attachment until later. 597 instanceId = "" 598 } else if err != nil { 599 return params.VolumeParams{}, err 600 } 601 volumeParams.Attachment = ¶ms.VolumeAttachmentParams{ 602 tag.String(), 603 machineTag.String(), 604 "", // volume ID 605 string(instanceId), 606 volumeParams.Provider, 607 volumeAttachmentParams.ReadOnly, 608 } 609 } 610 return volumeParams, nil 611 } 612 for i, arg := range args.Entities { 613 var result params.VolumeParamsResult 614 volumeParams, err := one(arg) 615 if err != nil { 616 result.Error = common.ServerError(err) 617 } else { 618 result.Result = volumeParams 619 } 620 results.Results[i] = result 621 } 622 return results, nil 623 } 624 625 // FilesystemParams returns the parameters for creating the filesystems 626 // with the specified tags. 627 func (s *StorageProvisionerAPI) FilesystemParams(args params.Entities) (params.FilesystemParamsResults, error) { 628 canAccess, err := s.getStorageEntityAuthFunc() 629 if err != nil { 630 return params.FilesystemParamsResults{}, err 631 } 632 modelConfig, err := s.st.ModelConfig() 633 if err != nil { 634 return params.FilesystemParamsResults{}, err 635 } 636 controllerCfg, err := s.st.ControllerConfig() 637 if err != nil { 638 return params.FilesystemParamsResults{}, err 639 } 640 results := params.FilesystemParamsResults{ 641 Results: make([]params.FilesystemParamsResult, len(args.Entities)), 642 } 643 one := func(arg params.Entity) (params.FilesystemParams, error) { 644 tag, err := names.ParseFilesystemTag(arg.Tag) 645 if err != nil || !canAccess(tag) { 646 return params.FilesystemParams{}, common.ErrPerm 647 } 648 filesystem, err := s.st.Filesystem(tag) 649 if errors.IsNotFound(err) { 650 return params.FilesystemParams{}, common.ErrPerm 651 } else if err != nil { 652 return params.FilesystemParams{}, err 653 } 654 storageInstance, err := storagecommon.MaybeAssignedStorageInstance( 655 filesystem.Storage, 656 s.st.StorageInstance, 657 ) 658 if err != nil { 659 return params.FilesystemParams{}, err 660 } 661 filesystemParams, err := storagecommon.FilesystemParams( 662 filesystem, storageInstance, modelConfig.UUID(), controllerCfg.ControllerUUID(), 663 modelConfig, s.poolManager, s.registry, 664 ) 665 if err != nil { 666 return params.FilesystemParams{}, err 667 } 668 return filesystemParams, nil 669 } 670 for i, arg := range args.Entities { 671 var result params.FilesystemParamsResult 672 filesystemParams, err := one(arg) 673 if err != nil { 674 result.Error = common.ServerError(err) 675 } else { 676 result.Result = filesystemParams 677 } 678 results.Results[i] = result 679 } 680 return results, nil 681 } 682 683 // VolumeAttachmentParams returns the parameters for creating the volume 684 // attachments with the specified IDs. 685 func (s *StorageProvisionerAPI) VolumeAttachmentParams( 686 args params.MachineStorageIds, 687 ) (params.VolumeAttachmentParamsResults, error) { 688 canAccess, err := s.getAttachmentAuthFunc() 689 if err != nil { 690 return params.VolumeAttachmentParamsResults{}, common.ServerError(common.ErrPerm) 691 } 692 results := params.VolumeAttachmentParamsResults{ 693 Results: make([]params.VolumeAttachmentParamsResult, len(args.Ids)), 694 } 695 one := func(arg params.MachineStorageId) (params.VolumeAttachmentParams, error) { 696 volumeAttachment, err := s.oneVolumeAttachment(arg, canAccess) 697 if err != nil { 698 return params.VolumeAttachmentParams{}, err 699 } 700 instanceId, err := s.st.MachineInstanceId(volumeAttachment.Machine()) 701 if errors.IsNotProvisioned(err) { 702 // The worker must watch for machine provisioning events. 703 instanceId = "" 704 } else if err != nil { 705 return params.VolumeAttachmentParams{}, err 706 } 707 volume, err := s.st.Volume(volumeAttachment.Volume()) 708 if err != nil { 709 return params.VolumeAttachmentParams{}, err 710 } 711 var volumeId string 712 var pool string 713 if volumeParams, ok := volume.Params(); ok { 714 pool = volumeParams.Pool 715 } else { 716 volumeInfo, err := volume.Info() 717 if err != nil { 718 return params.VolumeAttachmentParams{}, err 719 } 720 volumeId = volumeInfo.VolumeId 721 pool = volumeInfo.Pool 722 } 723 providerType, _, err := storagecommon.StoragePoolConfig(pool, s.poolManager, s.registry) 724 if err != nil { 725 return params.VolumeAttachmentParams{}, errors.Trace(err) 726 } 727 var readOnly bool 728 if volumeAttachmentParams, ok := volumeAttachment.Params(); ok { 729 readOnly = volumeAttachmentParams.ReadOnly 730 } else { 731 // Attachment parameters may be requested even if the 732 // attachment exists; i.e. for reattachment. 733 volumeAttachmentInfo, err := volumeAttachment.Info() 734 if err != nil { 735 return params.VolumeAttachmentParams{}, errors.Trace(err) 736 } 737 readOnly = volumeAttachmentInfo.ReadOnly 738 } 739 return params.VolumeAttachmentParams{ 740 volumeAttachment.Volume().String(), 741 volumeAttachment.Machine().String(), 742 volumeId, 743 string(instanceId), 744 string(providerType), 745 readOnly, 746 }, nil 747 } 748 for i, arg := range args.Ids { 749 var result params.VolumeAttachmentParamsResult 750 volumeAttachment, err := one(arg) 751 if err != nil { 752 result.Error = common.ServerError(err) 753 } else { 754 result.Result = volumeAttachment 755 } 756 results.Results[i] = result 757 } 758 return results, nil 759 } 760 761 // FilesystemAttachmentParams returns the parameters for creating the filesystem 762 // attachments with the specified IDs. 763 func (s *StorageProvisionerAPI) FilesystemAttachmentParams( 764 args params.MachineStorageIds, 765 ) (params.FilesystemAttachmentParamsResults, error) { 766 canAccess, err := s.getAttachmentAuthFunc() 767 if err != nil { 768 return params.FilesystemAttachmentParamsResults{}, common.ServerError(common.ErrPerm) 769 } 770 results := params.FilesystemAttachmentParamsResults{ 771 Results: make([]params.FilesystemAttachmentParamsResult, len(args.Ids)), 772 } 773 one := func(arg params.MachineStorageId) (params.FilesystemAttachmentParams, error) { 774 filesystemAttachment, err := s.oneFilesystemAttachment(arg, canAccess) 775 if err != nil { 776 return params.FilesystemAttachmentParams{}, err 777 } 778 instanceId, err := s.st.MachineInstanceId(filesystemAttachment.Machine()) 779 if errors.IsNotProvisioned(err) { 780 // The worker must watch for machine provisioning events. 781 instanceId = "" 782 } else if err != nil { 783 return params.FilesystemAttachmentParams{}, err 784 } 785 filesystem, err := s.st.Filesystem(filesystemAttachment.Filesystem()) 786 if err != nil { 787 return params.FilesystemAttachmentParams{}, err 788 } 789 var filesystemId string 790 var pool string 791 if filesystemParams, ok := filesystem.Params(); ok { 792 pool = filesystemParams.Pool 793 } else { 794 filesystemInfo, err := filesystem.Info() 795 if err != nil { 796 return params.FilesystemAttachmentParams{}, err 797 } 798 filesystemId = filesystemInfo.FilesystemId 799 pool = filesystemInfo.Pool 800 } 801 providerType, _, err := storagecommon.StoragePoolConfig(pool, s.poolManager, s.registry) 802 if err != nil { 803 return params.FilesystemAttachmentParams{}, errors.Trace(err) 804 } 805 var location string 806 var readOnly bool 807 if filesystemAttachmentParams, ok := filesystemAttachment.Params(); ok { 808 location = filesystemAttachmentParams.Location 809 readOnly = filesystemAttachmentParams.ReadOnly 810 } else { 811 // Attachment parameters may be requested even if the 812 // attachment exists; i.e. for reattachment. 813 filesystemAttachmentInfo, err := filesystemAttachment.Info() 814 if err != nil { 815 return params.FilesystemAttachmentParams{}, errors.Trace(err) 816 } 817 location = filesystemAttachmentInfo.MountPoint 818 readOnly = filesystemAttachmentInfo.ReadOnly 819 } 820 return params.FilesystemAttachmentParams{ 821 filesystemAttachment.Filesystem().String(), 822 filesystemAttachment.Machine().String(), 823 filesystemId, 824 string(instanceId), 825 string(providerType), 826 // TODO(axw) dealias MountPoint. We now have 827 // Path, MountPoint and Location in different 828 // parts of the codebase. 829 location, 830 readOnly, 831 }, nil 832 } 833 for i, arg := range args.Ids { 834 var result params.FilesystemAttachmentParamsResult 835 filesystemAttachment, err := one(arg) 836 if err != nil { 837 result.Error = common.ServerError(err) 838 } else { 839 result.Result = filesystemAttachment 840 } 841 results.Results[i] = result 842 } 843 return results, nil 844 } 845 846 func (s *StorageProvisionerAPI) oneVolumeAttachment( 847 id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool, 848 ) (state.VolumeAttachment, error) { 849 machineTag, err := names.ParseMachineTag(id.MachineTag) 850 if err != nil { 851 return nil, err 852 } 853 volumeTag, err := names.ParseVolumeTag(id.AttachmentTag) 854 if err != nil { 855 return nil, err 856 } 857 if !canAccess(machineTag, volumeTag) { 858 return nil, common.ErrPerm 859 } 860 volumeAttachment, err := s.st.VolumeAttachment(machineTag, volumeTag) 861 if errors.IsNotFound(err) { 862 return nil, common.ErrPerm 863 } else if err != nil { 864 return nil, err 865 } 866 return volumeAttachment, nil 867 } 868 869 func (s *StorageProvisionerAPI) oneVolumeBlockDevice( 870 id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool, 871 ) (state.BlockDeviceInfo, error) { 872 volumeAttachment, err := s.oneVolumeAttachment(id, canAccess) 873 if err != nil { 874 return state.BlockDeviceInfo{}, err 875 } 876 volume, err := s.st.Volume(volumeAttachment.Volume()) 877 if err != nil { 878 return state.BlockDeviceInfo{}, err 879 } 880 volumeInfo, err := volume.Info() 881 if err != nil { 882 return state.BlockDeviceInfo{}, err 883 } 884 volumeAttachmentInfo, err := volumeAttachment.Info() 885 if err != nil { 886 return state.BlockDeviceInfo{}, err 887 } 888 blockDevices, err := s.st.BlockDevices(volumeAttachment.Machine()) 889 if err != nil { 890 return state.BlockDeviceInfo{}, err 891 } 892 blockDevice, ok := storagecommon.MatchingBlockDevice( 893 blockDevices, 894 volumeInfo, 895 volumeAttachmentInfo, 896 ) 897 if !ok { 898 return state.BlockDeviceInfo{}, errors.NotFoundf( 899 "block device for volume %v on machine %v", 900 volumeAttachment.Volume().Id(), 901 volumeAttachment.Machine().Id(), 902 ) 903 } 904 return *blockDevice, nil 905 } 906 907 func (s *StorageProvisionerAPI) oneFilesystemAttachment( 908 id params.MachineStorageId, canAccess func(names.MachineTag, names.Tag) bool, 909 ) (state.FilesystemAttachment, error) { 910 machineTag, err := names.ParseMachineTag(id.MachineTag) 911 if err != nil { 912 return nil, err 913 } 914 filesystemTag, err := names.ParseFilesystemTag(id.AttachmentTag) 915 if err != nil { 916 return nil, err 917 } 918 if !canAccess(machineTag, filesystemTag) { 919 return nil, common.ErrPerm 920 } 921 filesystemAttachment, err := s.st.FilesystemAttachment(machineTag, filesystemTag) 922 if errors.IsNotFound(err) { 923 return nil, common.ErrPerm 924 } else if err != nil { 925 return nil, err 926 } 927 return filesystemAttachment, nil 928 } 929 930 // SetVolumeInfo records the details of newly provisioned volumes. 931 func (s *StorageProvisionerAPI) SetVolumeInfo(args params.Volumes) (params.ErrorResults, error) { 932 canAccessVolume, err := s.getStorageEntityAuthFunc() 933 if err != nil { 934 return params.ErrorResults{}, err 935 } 936 results := params.ErrorResults{ 937 Results: make([]params.ErrorResult, len(args.Volumes)), 938 } 939 one := func(arg params.Volume) error { 940 volumeTag, volumeInfo, err := storagecommon.VolumeToState(arg) 941 if err != nil { 942 return errors.Trace(err) 943 } else if !canAccessVolume(volumeTag) { 944 return common.ErrPerm 945 } 946 err = s.st.SetVolumeInfo(volumeTag, volumeInfo) 947 if errors.IsNotFound(err) { 948 return common.ErrPerm 949 } 950 return errors.Trace(err) 951 } 952 for i, arg := range args.Volumes { 953 err := one(arg) 954 results.Results[i].Error = common.ServerError(err) 955 } 956 return results, nil 957 } 958 959 // SetFilesystemInfo records the details of newly provisioned filesystems. 960 func (s *StorageProvisionerAPI) SetFilesystemInfo(args params.Filesystems) (params.ErrorResults, error) { 961 canAccessFilesystem, err := s.getStorageEntityAuthFunc() 962 if err != nil { 963 return params.ErrorResults{}, err 964 } 965 results := params.ErrorResults{ 966 Results: make([]params.ErrorResult, len(args.Filesystems)), 967 } 968 one := func(arg params.Filesystem) error { 969 filesystemTag, filesystemInfo, err := storagecommon.FilesystemToState(arg) 970 if err != nil { 971 return errors.Trace(err) 972 } else if !canAccessFilesystem(filesystemTag) { 973 return common.ErrPerm 974 } 975 err = s.st.SetFilesystemInfo(filesystemTag, filesystemInfo) 976 if errors.IsNotFound(err) { 977 return common.ErrPerm 978 } 979 return errors.Trace(err) 980 } 981 for i, arg := range args.Filesystems { 982 err := one(arg) 983 results.Results[i].Error = common.ServerError(err) 984 } 985 return results, nil 986 } 987 988 // SetVolumeAttachmentInfo records the details of newly provisioned volume 989 // attachments. 990 func (s *StorageProvisionerAPI) SetVolumeAttachmentInfo( 991 args params.VolumeAttachments, 992 ) (params.ErrorResults, error) { 993 canAccess, err := s.getAttachmentAuthFunc() 994 if err != nil { 995 return params.ErrorResults{}, err 996 } 997 results := params.ErrorResults{ 998 Results: make([]params.ErrorResult, len(args.VolumeAttachments)), 999 } 1000 one := func(arg params.VolumeAttachment) error { 1001 machineTag, volumeTag, volumeAttachmentInfo, err := storagecommon.VolumeAttachmentToState(arg) 1002 if err != nil { 1003 return errors.Trace(err) 1004 } 1005 if !canAccess(machineTag, volumeTag) { 1006 return common.ErrPerm 1007 } 1008 err = s.st.SetVolumeAttachmentInfo(machineTag, volumeTag, volumeAttachmentInfo) 1009 if errors.IsNotFound(err) { 1010 return common.ErrPerm 1011 } 1012 return errors.Trace(err) 1013 } 1014 for i, arg := range args.VolumeAttachments { 1015 err := one(arg) 1016 results.Results[i].Error = common.ServerError(err) 1017 } 1018 return results, nil 1019 } 1020 1021 // SetFilesystemAttachmentInfo records the details of newly provisioned filesystem 1022 // attachments. 1023 func (s *StorageProvisionerAPI) SetFilesystemAttachmentInfo( 1024 args params.FilesystemAttachments, 1025 ) (params.ErrorResults, error) { 1026 canAccess, err := s.getAttachmentAuthFunc() 1027 if err != nil { 1028 return params.ErrorResults{}, err 1029 } 1030 results := params.ErrorResults{ 1031 Results: make([]params.ErrorResult, len(args.FilesystemAttachments)), 1032 } 1033 one := func(arg params.FilesystemAttachment) error { 1034 machineTag, filesystemTag, filesystemAttachmentInfo, err := storagecommon.FilesystemAttachmentToState(arg) 1035 if err != nil { 1036 return errors.Trace(err) 1037 } 1038 if !canAccess(machineTag, filesystemTag) { 1039 return common.ErrPerm 1040 } 1041 err = s.st.SetFilesystemAttachmentInfo(machineTag, filesystemTag, filesystemAttachmentInfo) 1042 if errors.IsNotFound(err) { 1043 return common.ErrPerm 1044 } 1045 return errors.Trace(err) 1046 } 1047 for i, arg := range args.FilesystemAttachments { 1048 err := one(arg) 1049 results.Results[i].Error = common.ServerError(err) 1050 } 1051 return results, nil 1052 } 1053 1054 // AttachmentLife returns the lifecycle state of each specified machine 1055 // storage attachment. 1056 func (s *StorageProvisionerAPI) AttachmentLife(args params.MachineStorageIds) (params.LifeResults, error) { 1057 canAccess, err := s.getAttachmentAuthFunc() 1058 if err != nil { 1059 return params.LifeResults{}, err 1060 } 1061 results := params.LifeResults{ 1062 Results: make([]params.LifeResult, len(args.Ids)), 1063 } 1064 one := func(arg params.MachineStorageId) (params.Life, error) { 1065 machineTag, err := names.ParseMachineTag(arg.MachineTag) 1066 if err != nil { 1067 return "", err 1068 } 1069 attachmentTag, err := names.ParseTag(arg.AttachmentTag) 1070 if err != nil { 1071 return "", err 1072 } 1073 if !canAccess(machineTag, attachmentTag) { 1074 return "", common.ErrPerm 1075 } 1076 var lifer state.Lifer 1077 switch attachmentTag := attachmentTag.(type) { 1078 case names.VolumeTag: 1079 lifer, err = s.st.VolumeAttachment(machineTag, attachmentTag) 1080 case names.FilesystemTag: 1081 lifer, err = s.st.FilesystemAttachment(machineTag, attachmentTag) 1082 } 1083 if errors.IsNotFound(err) { 1084 return "", common.ErrPerm 1085 } else if err != nil { 1086 return "", errors.Trace(err) 1087 } 1088 return params.Life(lifer.Life().String()), nil 1089 } 1090 for i, arg := range args.Ids { 1091 life, err := one(arg) 1092 if err != nil { 1093 results.Results[i].Error = common.ServerError(err) 1094 } else { 1095 results.Results[i].Life = life 1096 } 1097 } 1098 return results, nil 1099 } 1100 1101 // Remove removes volumes and filesystems from state. 1102 func (s *StorageProvisionerAPI) Remove(args params.Entities) (params.ErrorResults, error) { 1103 canAccess, err := s.getStorageEntityAuthFunc() 1104 if err != nil { 1105 return params.ErrorResults{}, err 1106 } 1107 results := params.ErrorResults{ 1108 Results: make([]params.ErrorResult, len(args.Entities)), 1109 } 1110 one := func(arg params.Entity) error { 1111 tag, err := names.ParseTag(arg.Tag) 1112 if err != nil { 1113 return errors.Trace(err) 1114 } 1115 if !canAccess(tag) { 1116 return common.ErrPerm 1117 } 1118 switch tag := tag.(type) { 1119 case names.FilesystemTag: 1120 return s.st.RemoveFilesystem(tag) 1121 case names.VolumeTag: 1122 return s.st.RemoveVolume(tag) 1123 default: 1124 // should have been picked up by canAccess 1125 logger.Debugf("unexpected %v tag", tag.Kind()) 1126 return common.ErrPerm 1127 } 1128 } 1129 for i, arg := range args.Entities { 1130 err := one(arg) 1131 results.Results[i].Error = common.ServerError(err) 1132 } 1133 return results, nil 1134 } 1135 1136 // RemoveAttachments removes the specified machine storage attachments 1137 // from state. 1138 func (s *StorageProvisionerAPI) RemoveAttachment(args params.MachineStorageIds) (params.ErrorResults, error) { 1139 canAccess, err := s.getAttachmentAuthFunc() 1140 if err != nil { 1141 return params.ErrorResults{}, err 1142 } 1143 results := params.ErrorResults{ 1144 Results: make([]params.ErrorResult, len(args.Ids)), 1145 } 1146 removeAttachment := func(arg params.MachineStorageId) error { 1147 machineTag, err := names.ParseMachineTag(arg.MachineTag) 1148 if err != nil { 1149 return err 1150 } 1151 attachmentTag, err := names.ParseTag(arg.AttachmentTag) 1152 if err != nil { 1153 return err 1154 } 1155 if !canAccess(machineTag, attachmentTag) { 1156 return common.ErrPerm 1157 } 1158 switch attachmentTag := attachmentTag.(type) { 1159 case names.VolumeTag: 1160 return s.st.RemoveVolumeAttachment(machineTag, attachmentTag) 1161 case names.FilesystemTag: 1162 return s.st.RemoveFilesystemAttachment(machineTag, attachmentTag) 1163 default: 1164 return common.ErrPerm 1165 } 1166 } 1167 for i, arg := range args.Ids { 1168 if err := removeAttachment(arg); err != nil { 1169 results.Results[i].Error = common.ServerError(err) 1170 } 1171 } 1172 return results, nil 1173 }