github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/storageprovisioner/filesystems.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 "path/filepath" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/instance" 14 "github.com/juju/juju/storage" 15 ) 16 17 // filesystemsChanged is called when the lifecycle states of the filesystems 18 // with the provided IDs have been seen to have changed. 19 func filesystemsChanged(ctx *context, changes []string) error { 20 tags := make([]names.Tag, len(changes)) 21 for i, change := range changes { 22 tags[i] = names.NewFilesystemTag(change) 23 } 24 alive, dying, dead, err := storageEntityLife(ctx, tags) 25 if err != nil { 26 return errors.Trace(err) 27 } 28 logger.Debugf("filesystems alive: %v, dying: %v, dead: %v", alive, dying, dead) 29 if len(alive)+len(dying)+len(dead) == 0 { 30 return nil 31 } 32 33 // Get filesystem information for filesystems, so we can provision, 34 // deprovision, attach and detach. 35 filesystemTags := make([]names.FilesystemTag, 0, len(alive)+len(dying)+len(dead)) 36 for _, tag := range alive { 37 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 38 } 39 for _, tag := range dying { 40 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 41 } 42 for _, tag := range dead { 43 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 44 } 45 filesystemResults, err := ctx.filesystemAccessor.Filesystems(filesystemTags) 46 if err != nil { 47 return errors.Annotatef(err, "getting filesystem information") 48 } 49 50 aliveFilesystemTags := filesystemTags[:len(alive)] 51 dyingFilesystemTags := filesystemTags[len(alive) : len(alive)+len(dying)] 52 deadFilesystemTags := filesystemTags[len(alive)+len(dying):] 53 aliveFilesystemResults := filesystemResults[:len(alive)] 54 dyingFilesystemResults := filesystemResults[len(alive) : len(alive)+len(dying)] 55 deadFilesystemResults := filesystemResults[len(alive)+len(dying):] 56 57 if err := processDeadFilesystems(ctx, deadFilesystemTags, deadFilesystemResults); err != nil { 58 return errors.Annotate(err, "deprovisioning filesystems") 59 } 60 if err := processDyingFilesystems(ctx, dyingFilesystemTags, dyingFilesystemResults); err != nil { 61 return errors.Annotate(err, "processing dying filesystems") 62 } 63 if err := processAliveFilesystems(ctx, aliveFilesystemTags, aliveFilesystemResults); err != nil { 64 return errors.Annotate(err, "provisioning filesystems") 65 } 66 return nil 67 } 68 69 // filesystemAttachmentsChanged is called when the lifecycle states of the filesystem 70 // attachments with the provided IDs have been seen to have changed. 71 func filesystemAttachmentsChanged(ctx *context, ids []params.MachineStorageId) error { 72 alive, dying, dead, err := attachmentLife(ctx, ids) 73 if err != nil { 74 return errors.Trace(err) 75 } 76 logger.Debugf("filesystem attachment alive: %v, dying: %v, dead: %v", alive, dying, dead) 77 if len(dead) != 0 { 78 // We should not see dead filesystem attachments; 79 // attachments go directly from Dying to removed. 80 logger.Debugf("unexpected dead filesystem attachments: %v", dead) 81 } 82 if len(alive)+len(dying) == 0 { 83 return nil 84 } 85 86 // Get filesystem information for alive and dying filesystem attachments, so 87 // we can attach/detach. 88 ids = append(alive, dying...) 89 filesystemAttachmentResults, err := ctx.filesystemAccessor.FilesystemAttachments(ids) 90 if err != nil { 91 return errors.Annotatef(err, "getting filesystem attachment information") 92 } 93 94 // Deprovision Dying filesystem attachments. 95 dyingFilesystemAttachmentResults := filesystemAttachmentResults[len(alive):] 96 if err := processDyingFilesystemAttachments(ctx, dying, dyingFilesystemAttachmentResults); err != nil { 97 return errors.Annotate(err, "destroying filesystem attachments") 98 } 99 100 // Provision Alive filesystem attachments. 101 aliveFilesystemAttachmentResults := filesystemAttachmentResults[:len(alive)] 102 if err := processAliveFilesystemAttachments(ctx, alive, aliveFilesystemAttachmentResults); err != nil { 103 return errors.Annotate(err, "creating filesystem attachments") 104 } 105 106 return nil 107 } 108 109 // processDyingFilesystems processes the FilesystemResults for Dying filesystems, 110 // removing them from provisioning-pending as necessary, and storing the current 111 // filesystem info for provisioned filesystems so that attachments may be destroyed. 112 func processDyingFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 113 for _, tag := range tags { 114 delete(ctx.pendingFilesystems, tag) 115 } 116 for i, result := range filesystemResults { 117 tag := tags[i] 118 if result.Error == nil { 119 filesystem, err := filesystemFromParams(result.Result) 120 if err != nil { 121 return errors.Annotate(err, "getting filesystem info") 122 } 123 ctx.filesystems[tag] = filesystem 124 } else if !params.IsCodeNotProvisioned(result.Error) { 125 return errors.Annotatef(result.Error, "getting information for filesystem %s", tag.Id()) 126 } 127 } 128 return nil 129 } 130 131 // processDeadFilesystems processes the FilesystemResults for Dead filesystems, 132 // deprovisioning filesystems and removing from state as necessary. 133 func processDeadFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 134 for _, tag := range tags { 135 delete(ctx.pendingFilesystems, tag) 136 } 137 var destroy []names.FilesystemTag 138 var remove []names.Tag 139 for i, result := range filesystemResults { 140 tag := tags[i] 141 if result.Error == nil { 142 logger.Debugf("filesystem %s is provisioned, queuing for deprovisioning", tag.Id()) 143 filesystem, err := filesystemFromParams(result.Result) 144 if err != nil { 145 return errors.Annotate(err, "getting filesystem info") 146 } 147 ctx.filesystems[tag] = filesystem 148 destroy = append(destroy, tag) 149 continue 150 } 151 if params.IsCodeNotProvisioned(result.Error) { 152 logger.Debugf("filesystem %s is not provisioned, queuing for removal", tag.Id()) 153 remove = append(remove, tag) 154 continue 155 } 156 return errors.Annotatef(result.Error, "getting filesystem information for filesystem %s", tag.Id()) 157 } 158 if len(destroy)+len(remove) == 0 { 159 return nil 160 } 161 if len(destroy) > 0 { 162 errorResults, err := destroyFilesystems(ctx, destroy) 163 if err != nil { 164 return errors.Annotate(err, "destroying filesystems") 165 } 166 for i, tag := range destroy { 167 if err := errorResults[i]; err != nil { 168 return errors.Annotatef(err, "destroying %s", names.ReadableString(tag)) 169 } 170 remove = append(remove, tag) 171 } 172 } 173 if err := removeEntities(ctx, remove); err != nil { 174 return errors.Annotate(err, "removing filesystems from state") 175 } 176 return nil 177 } 178 179 // processDyingFilesystemAttachments processes the FilesystemAttachmentResults for 180 // Dying filesystem attachments, detaching filesystems and updating state as necessary. 181 func processDyingFilesystemAttachments( 182 ctx *context, 183 ids []params.MachineStorageId, 184 filesystemAttachmentResults []params.FilesystemAttachmentResult, 185 ) error { 186 if len(ids) == 0 { 187 return nil 188 } 189 for _, id := range ids { 190 delete(ctx.pendingFilesystemAttachments, id) 191 } 192 detach := make([]params.MachineStorageId, 0, len(ids)) 193 remove := make([]params.MachineStorageId, 0, len(ids)) 194 for i, result := range filesystemAttachmentResults { 195 id := ids[i] 196 if result.Error == nil { 197 detach = append(detach, id) 198 continue 199 } 200 if params.IsCodeNotProvisioned(result.Error) { 201 remove = append(remove, id) 202 continue 203 } 204 return errors.Annotatef(result.Error, "getting information for filesystem attachment %v", id) 205 } 206 if len(detach) > 0 { 207 attachmentParams, err := filesystemAttachmentParams(ctx, detach) 208 if err != nil { 209 return errors.Trace(err) 210 } 211 for i, params := range attachmentParams { 212 ctx.pendingDyingFilesystemAttachments[detach[i]] = params 213 } 214 } 215 if len(remove) > 0 { 216 if err := removeAttachments(ctx, remove); err != nil { 217 return errors.Annotate(err, "removing attachments from state") 218 } 219 } 220 return nil 221 } 222 223 // processAliveFilesystems processes the FilesystemResults for Alive filesystems, 224 // provisioning filesystems and setting the info in state as necessary. 225 func processAliveFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 226 // Filter out the already-provisioned filesystems. 227 pending := make([]names.FilesystemTag, 0, len(tags)) 228 for i, result := range filesystemResults { 229 tag := tags[i] 230 if result.Error == nil { 231 // Filesystem is already provisioned: skip. 232 logger.Debugf("filesystem %q is already provisioned, nothing to do", tag.Id()) 233 filesystem, err := filesystemFromParams(result.Result) 234 if err != nil { 235 return errors.Annotate(err, "getting filesystem info") 236 } 237 ctx.filesystems[tag] = filesystem 238 if filesystem.Volume != (names.VolumeTag{}) { 239 // Ensure that volume-backed filesystems' block 240 // devices are present even after creating the 241 // filesystem, so that attachments can be made. 242 maybeAddPendingVolumeBlockDevice(ctx, filesystem.Volume) 243 } 244 continue 245 } 246 if !params.IsCodeNotProvisioned(result.Error) { 247 return errors.Annotatef( 248 result.Error, "getting filesystem information for filesystem %q", tag.Id(), 249 ) 250 } 251 // The filesystem has not yet been provisioned, so record its tag 252 // to enquire about parameters below. 253 pending = append(pending, tag) 254 } 255 if len(pending) == 0 { 256 return nil 257 } 258 paramsResults, err := ctx.filesystemAccessor.FilesystemParams(pending) 259 if err != nil { 260 return errors.Annotate(err, "getting filesystem params") 261 } 262 for i, result := range paramsResults { 263 if result.Error != nil { 264 return errors.Annotate(result.Error, "getting filesystem parameters") 265 } 266 params, err := filesystemParamsFromParams(result.Result) 267 if err != nil { 268 return errors.Annotate(err, "getting filesystem parameters") 269 } 270 ctx.pendingFilesystems[pending[i]] = params 271 if params.Volume != (names.VolumeTag{}) { 272 // The filesystem is volume-backed: we must watch for 273 // the corresponding block device. This will trigger a 274 // one-time (for the volume) forced update of block 275 // devices. If the block device is not immediately 276 // available, then we rely on the watcher. The forced 277 // update is necessary in case the block device was 278 // added to state already, and we didn't observe it. 279 maybeAddPendingVolumeBlockDevice(ctx, params.Volume) 280 } 281 } 282 return nil 283 } 284 285 func maybeAddPendingVolumeBlockDevice(ctx *context, v names.VolumeTag) { 286 if _, ok := ctx.volumeBlockDevices[v]; !ok { 287 ctx.pendingVolumeBlockDevices.Add(v) 288 } 289 } 290 291 // processPendingFilesystems creates as many of the pending filesystems 292 // as possible, first ensuring that their prerequisites have been met. 293 func processPendingFilesystems(ctx *context) error { 294 if len(ctx.pendingFilesystems) == 0 { 295 logger.Tracef("no pending filesystems") 296 return nil 297 } 298 ready := make([]storage.FilesystemParams, 0, len(ctx.pendingFilesystems)) 299 for tag, filesystemParams := range ctx.pendingFilesystems { 300 if filesystemParams.Volume != (names.VolumeTag{}) { 301 // The filesystem is backed by a volume; ensure that 302 // the volume is attached by virtue of there being a 303 // matching block device on the machine. 304 if _, ok := ctx.volumeBlockDevices[filesystemParams.Volume]; !ok { 305 logger.Debugf( 306 "filesystem %v backing-volume %v is not attached yet", 307 filesystemParams.Tag.Id(), 308 filesystemParams.Volume.Id(), 309 ) 310 continue 311 } 312 } 313 ready = append(ready, filesystemParams) 314 delete(ctx.pendingFilesystems, tag) 315 } 316 if len(ready) == 0 { 317 return nil 318 } 319 filesystems, err := createFilesystems(ctx, ready) 320 if err != nil { 321 return errors.Annotate(err, "creating filesystems") 322 } 323 if err := setFilesystemInfo(ctx, filesystems); err != nil { 324 return errors.Trace(err) 325 } 326 return nil 327 } 328 329 func setFilesystemInfo(ctx *context, filesystems []storage.Filesystem) error { 330 if len(filesystems) == 0 { 331 return nil 332 } 333 // TODO(axw) we need to be able to list filesystems in the provider, 334 // by environment, so that we can "harvest" them if they're 335 // unknown. This will take care of killing filesystems that we fail 336 // to record in state. 337 errorResults, err := ctx.filesystemAccessor.SetFilesystemInfo( 338 filesystemsFromStorage(filesystems), 339 ) 340 if err != nil { 341 return errors.Annotate(err, "publishing filesystems to state") 342 } 343 for i, result := range errorResults { 344 if result.Error != nil { 345 return errors.Annotatef( 346 result.Error, "publishing filesystem %s to state", 347 filesystems[i].Tag.Id(), 348 ) 349 } 350 ctx.filesystems[filesystems[i].Tag] = filesystems[i] 351 } 352 return nil 353 } 354 355 // processAliveFilesystemAttachments processes the FilesystemAttachmentResults 356 // for Alive filesystem attachments, attaching filesystems and setting the info 357 // in state as necessary. 358 func processAliveFilesystemAttachments( 359 ctx *context, 360 ids []params.MachineStorageId, 361 filesystemAttachmentResults []params.FilesystemAttachmentResult, 362 ) error { 363 // Filter out the already-attached. 364 pending := make([]params.MachineStorageId, 0, len(ids)) 365 for i, result := range filesystemAttachmentResults { 366 if result.Error == nil { 367 delete(ctx.pendingFilesystemAttachments, ids[i]) 368 // Filesystem attachment is already provisioned: if we 369 // didn't (re)attach in this session, then we must do 370 // so now. 371 action := "nothing to do" 372 if _, ok := ctx.filesystemAttachments[ids[i]]; !ok { 373 // Not yet (re)attached in this session. 374 pending = append(pending, ids[i]) 375 action = "will reattach" 376 } 377 logger.Debugf( 378 "%s is already attached to %s, %s", 379 ids[i].AttachmentTag, ids[i].MachineTag, action, 380 ) 381 continue 382 } 383 if !params.IsCodeNotProvisioned(result.Error) { 384 return errors.Annotatef( 385 result.Error, "getting information for attachment %v", ids[i], 386 ) 387 } 388 // The filesystem has not yet been attached, so 389 // record its tag to enquire about parameters below. 390 pending = append(pending, ids[i]) 391 } 392 if len(pending) == 0 { 393 return nil 394 } 395 params, err := filesystemAttachmentParams(ctx, pending) 396 if err != nil { 397 return errors.Trace(err) 398 } 399 for i, params := range params { 400 if params.InstanceId == "" { 401 watchMachine(ctx, params.Machine) 402 } 403 ctx.pendingFilesystemAttachments[pending[i]] = params 404 } 405 return nil 406 } 407 408 // filesystemAttachmentParams obtains the specified attachments' parameters. 409 func filesystemAttachmentParams( 410 ctx *context, ids []params.MachineStorageId, 411 ) ([]storage.FilesystemAttachmentParams, error) { 412 paramsResults, err := ctx.filesystemAccessor.FilesystemAttachmentParams(ids) 413 if err != nil { 414 return nil, errors.Annotate(err, "getting filesystem attachment params") 415 } 416 attachmentParams := make([]storage.FilesystemAttachmentParams, len(ids)) 417 for i, result := range paramsResults { 418 if result.Error != nil { 419 return nil, errors.Annotate(result.Error, "getting filesystem attachment parameters") 420 } 421 params, err := filesystemAttachmentParamsFromParams(result.Result) 422 if err != nil { 423 return nil, errors.Annotate(err, "getting filesystem attachment parameters") 424 } 425 attachmentParams[i] = params 426 } 427 return attachmentParams, nil 428 } 429 430 func processPendingFilesystemAttachments(ctx *context) error { 431 if len(ctx.pendingFilesystemAttachments) == 0 { 432 logger.Tracef("no pending filesystem attachments") 433 return nil 434 } 435 ready := make([]storage.FilesystemAttachmentParams, 0, len(ctx.pendingFilesystemAttachments)) 436 for id, params := range ctx.pendingFilesystemAttachments { 437 filesystem, ok := ctx.filesystems[params.Filesystem] 438 if !ok { 439 logger.Debugf("filesystem %v has not been provisioned yet", params.Filesystem.Id()) 440 continue 441 } 442 if filesystem.Volume != (names.VolumeTag{}) { 443 // The filesystem is volume-backed: if the filesystem 444 // was created in another session, then the block device 445 // may not have been seen yet. We must wait for the block 446 // device watcher to trigger. 447 if _, ok := ctx.volumeBlockDevices[filesystem.Volume]; !ok { 448 logger.Debugf( 449 "filesystem %v backing-volume %v is not attached yet", 450 filesystem.Tag.Id(), 451 filesystem.Volume.Id(), 452 ) 453 continue 454 } 455 } 456 if params.InstanceId == "" { 457 logger.Debugf("machine %v has not been provisioned yet", params.Machine.Id()) 458 continue 459 } 460 if params.Path == "" { 461 params.Path = filepath.Join(ctx.storageDir, params.Filesystem.Id()) 462 } 463 params.FilesystemId = filesystem.FilesystemId 464 ready = append(ready, params) 465 delete(ctx.pendingFilesystemAttachments, id) 466 } 467 if len(ready) == 0 { 468 return nil 469 } 470 filesystemAttachments, err := createFilesystemAttachments(ctx, ready) 471 if err != nil { 472 return errors.Annotate(err, "creating filesystem attachments") 473 } 474 if err := setFilesystemAttachmentInfo(ctx, filesystemAttachments); err != nil { 475 return errors.Trace(err) 476 } 477 return nil 478 } 479 480 func processPendingDyingFilesystemAttachments(ctx *context) error { 481 if len(ctx.pendingDyingFilesystemAttachments) == 0 { 482 logger.Tracef("no pending, dying filesystem attachments") 483 return nil 484 } 485 var detach []storage.FilesystemAttachmentParams 486 var remove []params.MachineStorageId 487 for id, params := range ctx.pendingDyingFilesystemAttachments { 488 if _, ok := ctx.filesystems[params.Filesystem]; !ok { 489 // Wait until the filesystem info is known. 490 continue 491 } 492 delete(ctx.pendingDyingFilesystemAttachments, id) 493 detach = append(detach, params) 494 remove = append(remove, id) 495 } 496 if len(detach) == 0 { 497 return nil 498 } 499 if err := detachFilesystems(ctx, detach); err != nil { 500 return errors.Annotate(err, "detaching filesystems") 501 } 502 if err := removeAttachments(ctx, remove); err != nil { 503 return errors.Annotate(err, "removing attachments from state") 504 } 505 return nil 506 } 507 508 func setFilesystemAttachmentInfo(ctx *context, filesystemAttachments []storage.FilesystemAttachment) error { 509 if len(filesystemAttachments) == 0 { 510 return nil 511 } 512 // TODO(axw) we need to be able to list filesystem attachments in the 513 // provider, by environment, so that we can "harvest" them if they're 514 // unknown. This will take care of killing filesystems that we fail to 515 // record in state. 516 errorResults, err := ctx.filesystemAccessor.SetFilesystemAttachmentInfo( 517 filesystemAttachmentsFromStorage(filesystemAttachments), 518 ) 519 if err != nil { 520 return errors.Annotate(err, "publishing filesystems to state") 521 } 522 for i, result := range errorResults { 523 if result.Error != nil { 524 return errors.Annotatef( 525 result.Error, "publishing attachment of %s to %s to state", 526 names.ReadableString(filesystemAttachments[i].Filesystem), 527 names.ReadableString(filesystemAttachments[i].Machine), 528 ) 529 } 530 // Record the filesystem attachment in the context. 531 ctx.filesystemAttachments[params.MachineStorageId{ 532 MachineTag: filesystemAttachments[i].Machine.String(), 533 AttachmentTag: filesystemAttachments[i].Filesystem.String(), 534 }] = filesystemAttachments[i] 535 } 536 return nil 537 } 538 539 // createFilesystems creates filesystems with the specified parameters. 540 func createFilesystems(ctx *context, params []storage.FilesystemParams) ([]storage.Filesystem, error) { 541 // TODO(axw) later we may have multiple instantiations (sources) 542 // for a storage provider, e.g. multiple Ceph installations. For 543 // now we assume a single source for each provider type, with no 544 // configuration. 545 546 // Create filesystem sources. 547 filesystemSources := make(map[string]storage.FilesystemSource) 548 for _, params := range params { 549 sourceName := string(params.Provider) 550 if _, ok := filesystemSources[sourceName]; ok { 551 continue 552 } 553 if params.Volume != (names.VolumeTag{}) { 554 filesystemSources[sourceName] = ctx.managedFilesystemSource 555 continue 556 } 557 filesystemSource, err := filesystemSource( 558 ctx.environConfig, ctx.storageDir, sourceName, params.Provider, 559 ) 560 if err != nil { 561 return nil, errors.Annotate(err, "getting filesystem source") 562 } 563 filesystemSources[sourceName] = filesystemSource 564 } 565 566 // Validate and gather filesystem parameters. 567 paramsBySource := make(map[string][]storage.FilesystemParams) 568 for _, params := range params { 569 sourceName := string(params.Provider) 570 filesystemSource := filesystemSources[sourceName] 571 err := filesystemSource.ValidateFilesystemParams(params) 572 if err != nil { 573 // TODO(axw) we should set an error status for params.Tag 574 // here, and we should retry periodically. 575 logger.Errorf("ignoring invalid filesystem: %v", err) 576 continue 577 } 578 paramsBySource[sourceName] = append(paramsBySource[sourceName], params) 579 } 580 581 var allFilesystems []storage.Filesystem 582 for sourceName, params := range paramsBySource { 583 logger.Debugf("creating filesystems: %v", params) 584 filesystemSource := filesystemSources[sourceName] 585 results, err := filesystemSource.CreateFilesystems(params) 586 if err != nil { 587 return nil, errors.Annotatef(err, "creating filesystems from source %q", sourceName) 588 } 589 for i, result := range results { 590 if result.Error != nil { 591 return nil, errors.Annotatef(result.Error, "creating %s", names.ReadableString(params[i].Tag)) 592 } 593 allFilesystems = append(allFilesystems, *result.Filesystem) 594 } 595 } 596 return allFilesystems, nil 597 } 598 599 // createFilesystemAttachments creates filesystem attachments with the specified parameters. 600 func createFilesystemAttachments( 601 ctx *context, 602 params []storage.FilesystemAttachmentParams, 603 ) ([]storage.FilesystemAttachment, error) { 604 paramsBySource, filesystemSources, err := filesystemAttachmentParamsBySource(ctx, params) 605 if err != nil { 606 return nil, errors.Trace(err) 607 } 608 var allFilesystemAttachments []storage.FilesystemAttachment 609 for sourceName, params := range paramsBySource { 610 logger.Debugf("attaching filesystems: %v", params) 611 filesystemSource := filesystemSources[sourceName] 612 results, err := filesystemSource.AttachFilesystems(params) 613 if err != nil { 614 return nil, errors.Annotatef(err, "attaching filesystems from source %q", sourceName) 615 } 616 for i, result := range results { 617 if result.Error != nil { 618 return nil, errors.Annotatef( 619 err, "attaching %s to %s", 620 names.ReadableString(params[i].Filesystem), 621 names.ReadableString(params[i].Machine), 622 ) 623 } 624 allFilesystemAttachments = append(allFilesystemAttachments, *result.FilesystemAttachment) 625 } 626 } 627 return allFilesystemAttachments, nil 628 } 629 630 func destroyFilesystems(ctx *context, tags []names.FilesystemTag) ([]error, error) { 631 // TODO(axw) add storage.FilesystemSource.DestroyFilesystems 632 return make([]error, len(tags)), nil 633 } 634 635 func detachFilesystems(ctx *context, attachments []storage.FilesystemAttachmentParams) error { 636 paramsBySource, filesystemSources, err := filesystemAttachmentParamsBySource(ctx, attachments) 637 if err != nil { 638 return errors.Trace(err) 639 } 640 for sourceName, params := range paramsBySource { 641 logger.Debugf("detaching filesystems: %v", params) 642 filesystemSource := filesystemSources[sourceName] 643 results, err := filesystemSource.DetachFilesystems(params) 644 if err != nil { 645 return errors.Annotatef(err, "detaching filesystems from source %q", sourceName) 646 } 647 for i, err := range results { 648 if err == nil { 649 continue 650 } 651 return errors.Annotatef( 652 err, "detaching %s from %s", 653 names.ReadableString(params[i].Filesystem), 654 names.ReadableString(params[i].Machine), 655 ) 656 } 657 } 658 return nil 659 } 660 661 func filesystemAttachmentParamsBySource( 662 ctx *context, params []storage.FilesystemAttachmentParams, 663 ) (map[string][]storage.FilesystemAttachmentParams, map[string]storage.FilesystemSource, error) { 664 // TODO(axw) later we may have multiple instantiations (sources) 665 // for a storage provider, e.g. multiple Ceph installations. For 666 // now we assume a single source for each provider type, with no 667 // configuration. 668 filesystemSources := make(map[string]storage.FilesystemSource) 669 paramsBySource := make(map[string][]storage.FilesystemAttachmentParams) 670 for _, params := range params { 671 sourceName := string(params.Provider) 672 paramsBySource[sourceName] = append(paramsBySource[sourceName], params) 673 if _, ok := filesystemSources[sourceName]; ok { 674 continue 675 } 676 filesystem := ctx.filesystems[params.Filesystem] 677 if filesystem.Volume != (names.VolumeTag{}) { 678 filesystemSources[sourceName] = ctx.managedFilesystemSource 679 continue 680 } 681 filesystemSource, err := filesystemSource( 682 ctx.environConfig, ctx.storageDir, sourceName, params.Provider, 683 ) 684 if err != nil { 685 return nil, nil, errors.Annotate(err, "getting filesystem source") 686 } 687 filesystemSources[sourceName] = filesystemSource 688 } 689 return paramsBySource, filesystemSources, nil 690 } 691 692 func filesystemsFromStorage(in []storage.Filesystem) []params.Filesystem { 693 out := make([]params.Filesystem, len(in)) 694 for i, f := range in { 695 paramsFilesystem := params.Filesystem{ 696 f.Tag.String(), 697 "", 698 params.FilesystemInfo{ 699 f.FilesystemId, 700 f.Size, 701 }, 702 } 703 if f.Volume != (names.VolumeTag{}) { 704 paramsFilesystem.VolumeTag = f.Volume.String() 705 } 706 out[i] = paramsFilesystem 707 } 708 return out 709 } 710 711 func filesystemAttachmentsFromStorage(in []storage.FilesystemAttachment) []params.FilesystemAttachment { 712 out := make([]params.FilesystemAttachment, len(in)) 713 for i, f := range in { 714 out[i] = params.FilesystemAttachment{ 715 f.Filesystem.String(), 716 f.Machine.String(), 717 params.FilesystemAttachmentInfo{ 718 f.Path, 719 f.ReadOnly, 720 }, 721 } 722 } 723 return out 724 } 725 726 func filesystemFromParams(in params.Filesystem) (storage.Filesystem, error) { 727 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 728 if err != nil { 729 return storage.Filesystem{}, errors.Trace(err) 730 } 731 var volumeTag names.VolumeTag 732 if in.VolumeTag != "" { 733 volumeTag, err = names.ParseVolumeTag(in.VolumeTag) 734 if err != nil { 735 return storage.Filesystem{}, errors.Trace(err) 736 } 737 } 738 return storage.Filesystem{ 739 filesystemTag, 740 volumeTag, 741 storage.FilesystemInfo{ 742 in.Info.FilesystemId, 743 in.Info.Size, 744 }, 745 }, nil 746 } 747 748 func filesystemParamsFromParams(in params.FilesystemParams) (storage.FilesystemParams, error) { 749 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 750 if err != nil { 751 return storage.FilesystemParams{}, errors.Trace(err) 752 } 753 var volumeTag names.VolumeTag 754 if in.VolumeTag != "" { 755 volumeTag, err = names.ParseVolumeTag(in.VolumeTag) 756 if err != nil { 757 return storage.FilesystemParams{}, errors.Trace(err) 758 } 759 } 760 providerType := storage.ProviderType(in.Provider) 761 return storage.FilesystemParams{ 762 filesystemTag, 763 volumeTag, 764 in.Size, 765 providerType, 766 in.Attributes, 767 in.Tags, 768 }, nil 769 } 770 771 func filesystemAttachmentParamsFromParams(in params.FilesystemAttachmentParams) (storage.FilesystemAttachmentParams, error) { 772 machineTag, err := names.ParseMachineTag(in.MachineTag) 773 if err != nil { 774 return storage.FilesystemAttachmentParams{}, errors.Trace(err) 775 } 776 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 777 if err != nil { 778 return storage.FilesystemAttachmentParams{}, errors.Trace(err) 779 } 780 return storage.FilesystemAttachmentParams{ 781 AttachmentParams: storage.AttachmentParams{ 782 Provider: storage.ProviderType(in.Provider), 783 Machine: machineTag, 784 InstanceId: instance.Id(in.InstanceId), 785 ReadOnly: in.ReadOnly, 786 }, 787 Filesystem: filesystemTag, 788 FilesystemId: in.FilesystemId, 789 Path: in.MountPoint, 790 }, nil 791 }