github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/storageprovisioner/volumes.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/names" 9 10 "github.com/juju/juju/apiserver/params" 11 "github.com/juju/juju/environs/config" 12 "github.com/juju/juju/instance" 13 "github.com/juju/juju/storage" 14 ) 15 16 // volumesChanged is called when the lifecycle states of the volumes 17 // with the provided IDs have been seen to have changed. 18 func volumesChanged(ctx *context, changes []string) error { 19 tags := make([]names.Tag, len(changes)) 20 for i, change := range changes { 21 tags[i] = names.NewVolumeTag(change) 22 } 23 alive, dying, dead, err := storageEntityLife(ctx, tags) 24 if err != nil { 25 return errors.Trace(err) 26 } 27 logger.Debugf("volumes alive: %v, dying: %v, dead: %v", alive, dying, dead) 28 if err := processDyingVolumes(ctx, dying); err != nil { 29 return errors.Annotate(err, "processing dying volumes") 30 } 31 if len(alive)+len(dead) == 0 { 32 return nil 33 } 34 35 // Get volume information for alive and dead volumes, so 36 // we can provision/deprovision. 37 volumeTags := make([]names.VolumeTag, 0, len(alive)+len(dead)) 38 for _, tag := range alive { 39 volumeTags = append(volumeTags, tag.(names.VolumeTag)) 40 } 41 for _, tag := range dead { 42 volumeTags = append(volumeTags, tag.(names.VolumeTag)) 43 } 44 volumeResults, err := ctx.volumeAccessor.Volumes(volumeTags) 45 if err != nil { 46 return errors.Annotatef(err, "getting volume information") 47 } 48 if err := processDeadVolumes(ctx, volumeTags[len(alive):], volumeResults[len(alive):]); err != nil { 49 return errors.Annotate(err, "deprovisioning volumes") 50 } 51 if err := processAliveVolumes(ctx, alive, volumeResults[:len(alive)]); err != nil { 52 return errors.Annotate(err, "provisioning volumes") 53 } 54 return nil 55 } 56 57 // volumeAttachmentsChanged is called when the lifecycle states of the volume 58 // attachments with the provided IDs have been seen to have changed. 59 func volumeAttachmentsChanged(ctx *context, ids []params.MachineStorageId) error { 60 alive, dying, dead, err := attachmentLife(ctx, ids) 61 if err != nil { 62 return errors.Trace(err) 63 } 64 logger.Debugf("volume attachments alive: %v, dying: %v, dead: %v", alive, dying, dead) 65 if len(dead) != 0 { 66 // We should not see dead volume attachments; 67 // attachments go directly from Dying to removed. 68 logger.Debugf("unexpected dead volume attachments: %v", dead) 69 } 70 if len(alive)+len(dying) == 0 { 71 return nil 72 } 73 74 // Get volume information for alive and dying volume attachments, so 75 // we can attach/detach. 76 ids = append(alive, dying...) 77 volumeAttachmentResults, err := ctx.volumeAccessor.VolumeAttachments(ids) 78 if err != nil { 79 return errors.Annotatef(err, "getting volume attachment information") 80 } 81 82 // Deprovision Dying volume attachments. 83 dyingVolumeAttachmentResults := volumeAttachmentResults[len(alive):] 84 if err := processDyingVolumeAttachments(ctx, dying, dyingVolumeAttachmentResults); err != nil { 85 return errors.Annotate(err, "deprovisioning volume attachments") 86 } 87 88 // Provision Alive volume attachments. 89 aliveVolumeAttachmentResults := volumeAttachmentResults[:len(alive)] 90 if err := processAliveVolumeAttachments(ctx, alive, aliveVolumeAttachmentResults); err != nil { 91 return errors.Annotate(err, "provisioning volumes") 92 } 93 94 return nil 95 } 96 97 // processDyingVolumes processes the VolumeResults for Dying volumes, 98 // removing them from provisioning-pending as necessary. 99 func processDyingVolumes(ctx *context, tags []names.Tag) error { 100 for _, tag := range tags { 101 delete(ctx.pendingVolumes, tag.(names.VolumeTag)) 102 } 103 return nil 104 } 105 106 // processDeadVolumes processes the VolumeResults for Dead volumes, 107 // deprovisioning volumes and removing from state as necessary. 108 func processDeadVolumes(ctx *context, tags []names.VolumeTag, volumeResults []params.VolumeResult) error { 109 for _, tag := range tags { 110 delete(ctx.pendingVolumes, tag) 111 } 112 var destroy []names.VolumeTag 113 var remove []names.Tag 114 for i, result := range volumeResults { 115 tag := tags[i] 116 if result.Error == nil { 117 logger.Debugf("volume %s is provisioned, queuing for deprovisioning", tag.Id()) 118 volume, err := volumeFromParams(result.Result) 119 if err != nil { 120 return errors.Annotate(err, "getting volume info") 121 } 122 ctx.volumes[tag] = volume 123 destroy = append(destroy, tag) 124 continue 125 } 126 if params.IsCodeNotProvisioned(result.Error) { 127 logger.Debugf("volume %s is not provisioned, queuing for removal", tag.Id()) 128 remove = append(remove, tag) 129 continue 130 } 131 return errors.Annotatef(result.Error, "getting volume information for volume %s", tag.Id()) 132 } 133 if len(destroy)+len(remove) == 0 { 134 return nil 135 } 136 if len(destroy) > 0 { 137 errorResults, err := destroyVolumes(ctx, destroy) 138 if err != nil { 139 return errors.Annotate(err, "destroying volumes") 140 } 141 for i, tag := range destroy { 142 if err := errorResults[i]; err != nil { 143 return errors.Annotatef(err, "destroying %s", names.ReadableString(tag)) 144 } 145 remove = append(remove, tag) 146 } 147 } 148 if err := removeEntities(ctx, remove); err != nil { 149 return errors.Annotate(err, "removing volumes from state") 150 } 151 return nil 152 } 153 154 // processDyingVolumeAttachments processes the VolumeAttachmentResults for 155 // Dying volume attachments, detaching volumes and updating state as necessary. 156 func processDyingVolumeAttachments( 157 ctx *context, 158 ids []params.MachineStorageId, 159 volumeAttachmentResults []params.VolumeAttachmentResult, 160 ) error { 161 if len(ids) == 0 { 162 return nil 163 } 164 for _, id := range ids { 165 delete(ctx.pendingVolumeAttachments, id) 166 } 167 detach := make([]params.MachineStorageId, 0, len(ids)) 168 remove := make([]params.MachineStorageId, 0, len(ids)) 169 for i, result := range volumeAttachmentResults { 170 id := ids[i] 171 if result.Error == nil { 172 detach = append(detach, id) 173 continue 174 } 175 if params.IsCodeNotProvisioned(result.Error) { 176 remove = append(remove, id) 177 continue 178 } 179 return errors.Annotatef(result.Error, "getting information for volume attachment %v", id) 180 } 181 if len(detach) > 0 { 182 attachmentParams, err := volumeAttachmentParams(ctx, detach) 183 if err != nil { 184 return errors.Trace(err) 185 } 186 if err := detachVolumes(ctx, attachmentParams); err != nil { 187 return errors.Annotate(err, "detaching volumes") 188 } 189 remove = append(remove, detach...) 190 } 191 if err := removeAttachments(ctx, remove); err != nil { 192 return errors.Annotate(err, "removing attachments from state") 193 } 194 return nil 195 } 196 197 // processAliveVolumes processes the VolumeResults for Alive volumes, 198 // provisioning volumes and setting the info in state as necessary. 199 func processAliveVolumes(ctx *context, tags []names.Tag, volumeResults []params.VolumeResult) error { 200 // Filter out the already-provisioned volumes. 201 pending := make([]names.VolumeTag, 0, len(tags)) 202 for i, result := range volumeResults { 203 volumeTag := tags[i].(names.VolumeTag) 204 if result.Error == nil { 205 // Volume is already provisioned: skip. 206 logger.Debugf("volume %q is already provisioned, nothing to do", tags[i].Id()) 207 volume, err := volumeFromParams(result.Result) 208 if err != nil { 209 return errors.Annotate(err, "getting volume info") 210 } 211 ctx.volumes[volumeTag] = volume 212 delete(ctx.pendingVolumes, volumeTag) 213 continue 214 } 215 if !params.IsCodeNotProvisioned(result.Error) { 216 return errors.Annotatef( 217 result.Error, "getting volume information for volume %q", tags[i].Id(), 218 ) 219 } 220 // The volume has not yet been provisioned, so record its tag 221 // to enquire about parameters below. 222 pending = append(pending, volumeTag) 223 } 224 if len(pending) == 0 { 225 return nil 226 } 227 volumeParams, err := volumeParams(ctx, pending) 228 if err != nil { 229 return errors.Annotate(err, "getting volume params") 230 } 231 for i, params := range volumeParams { 232 if params.Attachment.InstanceId == "" { 233 watchMachine(ctx, params.Attachment.Machine) 234 } 235 ctx.pendingVolumes[pending[i]] = params 236 } 237 return nil 238 } 239 240 // processPendingVolumes creates as many of the pending volumes as possible, 241 // first ensuring that their prerequisites have been met. 242 func processPendingVolumes(ctx *context) error { 243 if len(ctx.pendingVolumes) == 0 { 244 logger.Tracef("no pending volumes") 245 return nil 246 } 247 ready := make([]storage.VolumeParams, 0, len(ctx.pendingVolumes)) 248 for tag, volumeParams := range ctx.pendingVolumes { 249 if volumeParams.Attachment.InstanceId == "" { 250 logger.Debugf("machine %v has not been provisioned yet", volumeParams.Attachment.Machine.Id()) 251 continue 252 } 253 ready = append(ready, volumeParams) 254 delete(ctx.pendingVolumes, tag) 255 } 256 if len(ready) == 0 { 257 return nil 258 } 259 volumes, volumeAttachments, err := createVolumes(ctx.environConfig, ctx.storageDir, ready) 260 if err != nil { 261 return errors.Annotate(err, "creating volumes") 262 } 263 if len(volumes) == 0 { 264 return nil 265 } 266 // TODO(axw) we need to be able to list volumes in the provider, 267 // by environment, so that we can "harvest" them if they're 268 // unknown. This will take care of killing volumes that we fail 269 // to record in state. 270 errorResults, err := ctx.volumeAccessor.SetVolumeInfo(volumesFromStorage(volumes)) 271 if err != nil { 272 return errors.Annotate(err, "publishing volumes to state") 273 } 274 for i, result := range errorResults { 275 if result.Error != nil { 276 return errors.Annotatef( 277 result.Error, "publishing volume %s to state", 278 volumes[i].Tag.Id(), 279 ) 280 } 281 } 282 for _, v := range volumes { 283 ctx.volumes[v.Tag] = v 284 } 285 // Note: the storage provisioner that creates a volume is also 286 // responsible for creating the volume attachment. It is therefore 287 // safe to set the volume attachment info after the volume info, 288 // without leading to the possibility of concurrent, duplicate 289 // attachments. 290 err = setVolumeAttachmentInfo(ctx, volumeAttachments) 291 if err != nil { 292 return errors.Trace(err) 293 } 294 return nil 295 } 296 297 // processAliveVolumeAttachments processes the VolumeAttachmentResults 298 // for Alive volume attachments, attaching volumes and setting the info 299 // in state as necessary. 300 func processAliveVolumeAttachments( 301 ctx *context, 302 ids []params.MachineStorageId, 303 volumeAttachmentResults []params.VolumeAttachmentResult, 304 ) error { 305 // Filter out the already-attached. 306 pending := make([]params.MachineStorageId, 0, len(ids)) 307 for i, result := range volumeAttachmentResults { 308 if result.Error == nil { 309 delete(ctx.pendingVolumeAttachments, ids[i]) 310 // Volume attachment is already provisioned: if we 311 // didn't (re)attach in this session, then we must 312 // do so now. 313 action := "nothing to do" 314 if _, ok := ctx.volumeAttachments[ids[i]]; !ok { 315 // Not yet (re)attached in this session. 316 pending = append(pending, ids[i]) 317 action = "will reattach" 318 } 319 logger.Debugf( 320 "%s is already attached to %s, %s", 321 ids[i].AttachmentTag, ids[i].MachineTag, action, 322 ) 323 continue 324 } 325 if !params.IsCodeNotProvisioned(result.Error) { 326 return errors.Annotatef( 327 result.Error, "getting information for attachment %v", ids[i], 328 ) 329 } 330 // The volume has not yet been provisioned, so record its tag 331 // to enquire about parameters below. 332 pending = append(pending, ids[i]) 333 } 334 if len(pending) == 0 { 335 return nil 336 } 337 params, err := volumeAttachmentParams(ctx, pending) 338 if err != nil { 339 return errors.Trace(err) 340 } 341 for i, params := range params { 342 if params.InstanceId == "" { 343 watchMachine(ctx, params.Machine) 344 } 345 ctx.pendingVolumeAttachments[pending[i]] = params 346 } 347 return nil 348 } 349 350 // volumeAttachmentParams obtains the specified attachments' parameters. 351 func volumeAttachmentParams( 352 ctx *context, ids []params.MachineStorageId, 353 ) ([]storage.VolumeAttachmentParams, error) { 354 paramsResults, err := ctx.volumeAccessor.VolumeAttachmentParams(ids) 355 if err != nil { 356 return nil, errors.Annotate(err, "getting volume attachment params") 357 } 358 attachmentParams := make([]storage.VolumeAttachmentParams, len(ids)) 359 for i, result := range paramsResults { 360 if result.Error != nil { 361 return nil, errors.Annotate(result.Error, "getting volume attachment parameters") 362 } 363 params, err := volumeAttachmentParamsFromParams(result.Result) 364 if err != nil { 365 return nil, errors.Annotate(err, "getting volume attachment parameters") 366 } 367 attachmentParams[i] = params 368 } 369 return attachmentParams, nil 370 } 371 372 // processPendingVolumeAttachments creates as many of the pending volume 373 // attachments as possible, first ensuring that their prerequisites have 374 // been met. 375 func processPendingVolumeAttachments(ctx *context) error { 376 if len(ctx.pendingVolumeAttachments) == 0 { 377 logger.Tracef("no pending volume attachments") 378 return nil 379 } 380 ready := make([]storage.VolumeAttachmentParams, 0, len(ctx.pendingVolumeAttachments)) 381 for id, params := range ctx.pendingVolumeAttachments { 382 volume, ok := ctx.volumes[params.Volume] 383 if !ok { 384 // volume hasn't been provisioned yet 385 logger.Debugf("volume %v has not been provisioned yet", params.Volume.Id()) 386 continue 387 } 388 if params.InstanceId == "" { 389 logger.Debugf("machine %v has not been provisioned yet", params.Machine.Id()) 390 continue 391 } 392 params.VolumeId = volume.VolumeId 393 ready = append(ready, params) 394 delete(ctx.pendingVolumeAttachments, id) 395 } 396 if len(ready) == 0 { 397 return nil 398 } 399 volumeAttachments, err := createVolumeAttachments(ctx.environConfig, ctx.storageDir, ready) 400 if err != nil { 401 return errors.Annotate(err, "creating volume attachments") 402 } 403 if err := setVolumeAttachmentInfo(ctx, volumeAttachments); err != nil { 404 return errors.Trace(err) 405 } 406 return nil 407 } 408 409 // createVolumes creates volumes with the specified parameters. 410 func createVolumes( 411 environConfig *config.Config, 412 baseStorageDir string, 413 params []storage.VolumeParams, 414 ) ([]storage.Volume, []storage.VolumeAttachment, error) { 415 paramsBySource, volumeSources, err := volumeParamsBySource( 416 environConfig, baseStorageDir, params, 417 ) 418 if err != nil { 419 return nil, nil, errors.Trace(err) 420 } 421 var allVolumes []storage.Volume 422 var allVolumeAttachments []storage.VolumeAttachment 423 for sourceName, params := range paramsBySource { 424 logger.Debugf("creating volumes: %v", params) 425 volumeSource := volumeSources[sourceName] 426 volumes, volumeAttachments, err := volumeSource.CreateVolumes(params) 427 if err != nil { 428 return nil, nil, errors.Annotatef(err, "creating volumes from source %q", sourceName) 429 } 430 allVolumes = append(allVolumes, volumes...) 431 allVolumeAttachments = append(allVolumeAttachments, volumeAttachments...) 432 } 433 return allVolumes, allVolumeAttachments, nil 434 } 435 436 // createVolumeAttachments creates volume attachments with the specified parameters. 437 func createVolumeAttachments( 438 environConfig *config.Config, 439 baseStorageDir string, 440 params []storage.VolumeAttachmentParams, 441 ) ([]storage.VolumeAttachment, error) { 442 paramsBySource, volumeSources, err := volumeAttachmentParamsBySource( 443 environConfig, baseStorageDir, params, 444 ) 445 if err != nil { 446 return nil, errors.Trace(err) 447 } 448 var allVolumeAttachments []storage.VolumeAttachment 449 for sourceName, params := range paramsBySource { 450 logger.Debugf("attaching volumes: %v", params) 451 volumeSource := volumeSources[sourceName] 452 volumeAttachments, err := volumeSource.AttachVolumes(params) 453 if err != nil { 454 return nil, errors.Annotatef(err, "attaching volumes from source %q", sourceName) 455 } 456 allVolumeAttachments = append(allVolumeAttachments, volumeAttachments...) 457 } 458 return allVolumeAttachments, nil 459 } 460 461 func setVolumeAttachmentInfo(ctx *context, volumeAttachments []storage.VolumeAttachment) error { 462 if len(volumeAttachments) == 0 { 463 return nil 464 } 465 // TODO(axw) we need to be able to list volume attachments in the 466 // provider, by environment, so that we can "harvest" them if they're 467 // unknown. This will take care of killing volumes that we fail to 468 // record in state. 469 errorResults, err := ctx.volumeAccessor.SetVolumeAttachmentInfo( 470 volumeAttachmentsFromStorage(volumeAttachments), 471 ) 472 if err != nil { 473 return errors.Annotate(err, "publishing volumes to state") 474 } 475 for i, result := range errorResults { 476 if result.Error != nil { 477 return errors.Annotatef( 478 result.Error, "publishing attachment of %s to %s to state", 479 names.ReadableString(volumeAttachments[i].Volume), 480 names.ReadableString(volumeAttachments[i].Machine), 481 ) 482 } 483 // Record the volume attachment in the context. 484 ctx.volumeAttachments[params.MachineStorageId{ 485 MachineTag: volumeAttachments[i].Machine.String(), 486 AttachmentTag: volumeAttachments[i].Volume.String(), 487 }] = volumeAttachments[i] 488 } 489 return nil 490 } 491 492 func destroyVolumes(ctx *context, tags []names.VolumeTag) ([]error, error) { 493 volumeParams, err := volumeParams(ctx, tags) 494 if err != nil { 495 return nil, errors.Trace(err) 496 } 497 paramsBySource, volumeSources, err := volumeParamsBySource( 498 ctx.environConfig, ctx.storageDir, volumeParams, 499 ) 500 if err != nil { 501 return nil, errors.Trace(err) 502 } 503 var errs []error 504 for sourceName, params := range paramsBySource { 505 logger.Debugf("destroying volumes from %q: %v", sourceName, params) 506 volumeSource := volumeSources[sourceName] 507 volumeIds := make([]string, len(params)) 508 for i, params := range params { 509 volume, ok := ctx.volumes[params.Tag] 510 if !ok { 511 return nil, errors.NotFoundf("volume %s", params.Tag.Id()) 512 } 513 volumeIds[i] = volume.VolumeId 514 } 515 errs = append(errs, volumeSource.DestroyVolumes(volumeIds)...) 516 } 517 return errs, nil 518 } 519 520 // volumeParams obtains the specified volumes' parameters. 521 func volumeParams(ctx *context, tags []names.VolumeTag) ([]storage.VolumeParams, error) { 522 paramsResults, err := ctx.volumeAccessor.VolumeParams(tags) 523 if err != nil { 524 return nil, errors.Annotate(err, "getting volume params") 525 } 526 allParams := make([]storage.VolumeParams, len(tags)) 527 for i, result := range paramsResults { 528 if result.Error != nil { 529 return nil, errors.Annotate(result.Error, "getting volume parameters") 530 } 531 params, err := volumeParamsFromParams(result.Result) 532 if err != nil { 533 return nil, errors.Annotate(err, "getting volume parameters") 534 } 535 allParams[i] = params 536 } 537 return allParams, nil 538 } 539 540 func volumeParamsBySource( 541 environConfig *config.Config, 542 baseStorageDir string, 543 params []storage.VolumeParams, 544 ) (map[string][]storage.VolumeParams, map[string]storage.VolumeSource, error) { 545 // TODO(axw) later we may have multiple instantiations (sources) 546 // for a storage provider, e.g. multiple Ceph installations. For 547 // now we assume a single source for each provider type, with no 548 // configuration. 549 volumeSources := make(map[string]storage.VolumeSource) 550 for _, params := range params { 551 sourceName := string(params.Provider) 552 if _, ok := volumeSources[sourceName]; ok { 553 continue 554 } 555 volumeSource, err := volumeSource( 556 environConfig, baseStorageDir, sourceName, params.Provider, 557 ) 558 if errors.Cause(err) == errNonDynamic { 559 volumeSource = nil 560 } else if err != nil { 561 return nil, nil, errors.Annotate(err, "getting volume source") 562 } 563 volumeSources[sourceName] = volumeSource 564 } 565 paramsBySource := make(map[string][]storage.VolumeParams) 566 for _, params := range params { 567 sourceName := string(params.Provider) 568 volumeSource := volumeSources[sourceName] 569 if volumeSource == nil { 570 // Ignore nil volume sources; this means that the 571 // volume should be created by the machine-provisioner. 572 continue 573 } 574 err := volumeSource.ValidateVolumeParams(params) 575 switch errors.Cause(err) { 576 case nil: 577 paramsBySource[sourceName] = append(paramsBySource[sourceName], params) 578 default: 579 return nil, nil, errors.Annotatef(err, "invalid parameters for volume %s", params.Tag.Id()) 580 } 581 } 582 return paramsBySource, volumeSources, nil 583 } 584 585 func detachVolumes(ctx *context, attachments []storage.VolumeAttachmentParams) error { 586 paramsBySource, volumeSources, err := volumeAttachmentParamsBySource( 587 ctx.environConfig, ctx.storageDir, attachments, 588 ) 589 if err != nil { 590 return errors.Trace(err) 591 } 592 for sourceName, params := range paramsBySource { 593 logger.Debugf("detaching volumes: %v", params) 594 volumeSource := volumeSources[sourceName] 595 if err := volumeSource.DetachVolumes(params); err != nil { 596 return errors.Annotatef(err, "detaching volumes from source %q", sourceName) 597 } 598 } 599 return nil 600 } 601 602 func volumeAttachmentParamsBySource( 603 environConfig *config.Config, 604 baseStorageDir string, 605 params []storage.VolumeAttachmentParams, 606 ) (map[string][]storage.VolumeAttachmentParams, map[string]storage.VolumeSource, error) { 607 // TODO(axw) later we may have multiple instantiations (sources) 608 // for a storage provider, e.g. multiple Ceph installations. For 609 // now we assume a single source for each provider type, with no 610 // configuration. 611 volumeSources := make(map[string]storage.VolumeSource) 612 paramsBySource := make(map[string][]storage.VolumeAttachmentParams) 613 for _, params := range params { 614 sourceName := string(params.Provider) 615 paramsBySource[sourceName] = append(paramsBySource[sourceName], params) 616 if _, ok := volumeSources[sourceName]; ok { 617 continue 618 } 619 volumeSource, err := volumeSource( 620 environConfig, baseStorageDir, sourceName, params.Provider, 621 ) 622 if err != nil { 623 return nil, nil, errors.Annotate(err, "getting volume source") 624 } 625 volumeSources[sourceName] = volumeSource 626 } 627 return paramsBySource, volumeSources, nil 628 } 629 630 func volumesFromStorage(in []storage.Volume) []params.Volume { 631 out := make([]params.Volume, len(in)) 632 for i, v := range in { 633 out[i] = params.Volume{ 634 v.Tag.String(), 635 params.VolumeInfo{ 636 v.VolumeId, 637 v.HardwareId, 638 v.Size, 639 v.Persistent, 640 }, 641 } 642 } 643 return out 644 } 645 646 func volumeAttachmentsFromStorage(in []storage.VolumeAttachment) []params.VolumeAttachment { 647 out := make([]params.VolumeAttachment, len(in)) 648 for i, v := range in { 649 out[i] = params.VolumeAttachment{ 650 v.Volume.String(), 651 v.Machine.String(), 652 params.VolumeAttachmentInfo{ 653 v.DeviceName, 654 v.ReadOnly, 655 }, 656 } 657 } 658 return out 659 } 660 661 func volumeFromParams(in params.Volume) (storage.Volume, error) { 662 volumeTag, err := names.ParseVolumeTag(in.VolumeTag) 663 if err != nil { 664 return storage.Volume{}, errors.Trace(err) 665 } 666 return storage.Volume{ 667 volumeTag, 668 storage.VolumeInfo{ 669 in.Info.VolumeId, 670 in.Info.HardwareId, 671 in.Info.Size, 672 in.Info.Persistent, 673 }, 674 }, nil 675 } 676 677 func volumeAttachmentFromParams(in params.VolumeAttachment) (storage.VolumeAttachment, error) { 678 volumeTag, err := names.ParseVolumeTag(in.VolumeTag) 679 if err != nil { 680 return storage.VolumeAttachment{}, errors.Trace(err) 681 } 682 machineTag, err := names.ParseMachineTag(in.MachineTag) 683 if err != nil { 684 return storage.VolumeAttachment{}, errors.Trace(err) 685 } 686 return storage.VolumeAttachment{ 687 volumeTag, 688 machineTag, 689 storage.VolumeAttachmentInfo{ 690 in.Info.DeviceName, 691 in.Info.ReadOnly, 692 }, 693 }, nil 694 } 695 696 func volumeParamsFromParams(in params.VolumeParams) (storage.VolumeParams, error) { 697 volumeTag, err := names.ParseVolumeTag(in.VolumeTag) 698 if err != nil { 699 return storage.VolumeParams{}, errors.Trace(err) 700 } 701 providerType := storage.ProviderType(in.Provider) 702 703 var attachment *storage.VolumeAttachmentParams 704 if in.Attachment != nil { 705 if in.Attachment.Provider != in.Provider { 706 return storage.VolumeParams{}, errors.Errorf( 707 "storage provider mismatch: volume (%q), attachment (%q)", 708 in.Provider, in.Attachment.Provider, 709 ) 710 } 711 if in.Attachment.VolumeTag != in.VolumeTag { 712 return storage.VolumeParams{}, errors.Errorf( 713 "volume tag mismatch: volume (%q), attachment (%q)", 714 in.VolumeTag, in.Attachment.VolumeTag, 715 ) 716 } 717 machineTag, err := names.ParseMachineTag(in.Attachment.MachineTag) 718 if err != nil { 719 return storage.VolumeParams{}, errors.Annotate( 720 err, "parsing attachment machine tag", 721 ) 722 } 723 attachment = &storage.VolumeAttachmentParams{ 724 AttachmentParams: storage.AttachmentParams{ 725 Provider: providerType, 726 Machine: machineTag, 727 InstanceId: instance.Id(in.Attachment.InstanceId), 728 ReadOnly: in.Attachment.ReadOnly, 729 }, 730 Volume: volumeTag, 731 } 732 } 733 return storage.VolumeParams{ 734 volumeTag, 735 in.Size, 736 providerType, 737 in.Attributes, 738 in.Tags, 739 attachment, 740 }, nil 741 } 742 743 func volumeAttachmentParamsFromParams(in params.VolumeAttachmentParams) (storage.VolumeAttachmentParams, error) { 744 machineTag, err := names.ParseMachineTag(in.MachineTag) 745 if err != nil { 746 return storage.VolumeAttachmentParams{}, errors.Trace(err) 747 } 748 volumeTag, err := names.ParseVolumeTag(in.VolumeTag) 749 if err != nil { 750 return storage.VolumeAttachmentParams{}, errors.Trace(err) 751 } 752 return storage.VolumeAttachmentParams{ 753 AttachmentParams: storage.AttachmentParams{ 754 Provider: storage.ProviderType(in.Provider), 755 Machine: machineTag, 756 InstanceId: instance.Id(in.InstanceId), 757 ReadOnly: in.ReadOnly, 758 }, 759 Volume: volumeTag, 760 VolumeId: in.VolumeId, 761 }, nil 762 }