github.com/candidpartners/terraform@v0.9.5-0.20171005231213-29f5f88820f6/builtin/providers/netapp/resource_cloud_volume.go (about) 1 package netapp 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 "github.com/candidpartners/occm-sdk-go/api/workenv" 9 "github.com/candidpartners/occm-sdk-go/api/workenv/vsa" 10 "github.com/candidpartners/occm-sdk-go/util" 11 "github.com/hashicorp/terraform/helper/schema" 12 "github.com/hashicorp/terraform/helper/validation" 13 ) 14 15 func resourceCloudVolume() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceCloudVolumeCreate, 18 Read: resourceCloudVolumeRead, 19 Update: resourceCloudVolumeUpdate, 20 Delete: resourceCloudVolumeDelete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 Schema: map[string]*schema.Schema{ 25 "workenv_id": { 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: true, 29 }, 30 "svm_name": { 31 Type: schema.TypeString, 32 Required: true, 33 ForceNew: true, 34 }, 35 "aggregate_name": { 36 Type: schema.TypeString, 37 Optional: true, 38 Computed: true, 39 }, 40 "name": { 41 Type: schema.TypeString, 42 Required: true, 43 ForceNew: true, 44 }, 45 "type": { 46 Type: schema.TypeString, 47 Required: true, 48 ForceNew: true, 49 ValidateFunc: validation.StringInSlice([]string{ 50 "nfs", 51 "cifs", 52 }, false), 53 }, 54 "size": { 55 Type: schema.TypeFloat, 56 Required: true, 57 ForceNew: true, 58 }, 59 "size_unit": { 60 Type: schema.TypeString, 61 Required: true, 62 ForceNew: true, 63 ValidateFunc: validation.StringInSlice([]string{ 64 "GB", 65 "TB", 66 }, true), 67 }, 68 "initial_size": { 69 Type: schema.TypeFloat, 70 Optional: true, 71 }, 72 "initial_size_unit": { 73 Type: schema.TypeString, 74 Optional: true, 75 ValidateFunc: validation.StringInSlice([]string{ 76 "GB", 77 "TB", 78 }, true), 79 }, 80 "snapshot_policy": { 81 Type: schema.TypeString, 82 Required: true, 83 }, 84 "export_policy": { 85 Type: schema.TypeList, 86 Optional: true, 87 Computed: true, 88 ConflictsWith: []string{"share"}, 89 Elem: &schema.Schema{ 90 Type: schema.TypeString, 91 }, 92 }, 93 "share": { 94 Type: schema.TypeList, 95 Optional: true, 96 Computed: true, 97 MinItems: 1, 98 MaxItems: 1, 99 Elem: &schema.Resource{ 100 Schema: map[string]*schema.Schema{ 101 "name": { 102 Type: schema.TypeString, 103 Required: true, 104 }, 105 "permission": { 106 Type: schema.TypeList, 107 MinItems: 1, 108 Required: true, 109 Elem: &schema.Resource{ 110 Schema: map[string]*schema.Schema{ 111 "type": { 112 Type: schema.TypeString, 113 Required: true, 114 ValidateFunc: validation.StringInSlice([]string{ 115 "read", 116 "change", 117 "full_control", 118 "no_access", 119 }, false), 120 }, 121 "users": { 122 Type: schema.TypeList, 123 Required: true, 124 MinItems: 1, 125 Elem: &schema.Schema{ 126 Type: schema.TypeString, 127 }, 128 }, 129 }, 130 }, 131 }, 132 }, 133 }, 134 }, 135 "thin_provisioning": { 136 Type: schema.TypeBool, 137 Optional: true, 138 ForceNew: true, 139 Default: true, 140 }, 141 "compression": { 142 Type: schema.TypeBool, 143 Optional: true, 144 ForceNew: true, 145 Default: true, 146 }, 147 "deduplication": { 148 Type: schema.TypeBool, 149 Optional: true, 150 ForceNew: true, 151 Default: true, 152 }, 153 "max_num_disks_approved_to_add": { 154 Type: schema.TypeInt, 155 Optional: true, 156 }, 157 "verify_name_uniqueness": { 158 Type: schema.TypeBool, 159 Optional: true, 160 }, 161 "provider_volume_type": { 162 Type: schema.TypeString, 163 Optional: true, 164 Default: "gp2", 165 ValidateFunc: validation.StringInSlice([]string{ 166 "gp2", 167 "st1", 168 "io1", 169 "sc1", 170 }, false), 171 }, 172 "iops": { 173 Type: schema.TypeInt, 174 Optional: true, 175 }, 176 "sync_to_s3": { 177 Type: schema.TypeBool, 178 Optional: true, 179 }, 180 "capacity_tier": { 181 Type: schema.TypeString, 182 Optional: true, 183 }, 184 "create_aggregate_if_not_found": { 185 Type: schema.TypeBool, 186 Optional: true, 187 }, 188 }, 189 } 190 } 191 192 func buildVolumeQuoteRequest(d *schema.ResourceData) *vsa.VSAVolumeQuoteRequest { 193 req := vsa.VSAVolumeQuoteRequest{ 194 WorkingEnvironmentId: d.Get("workenv_id").(string), 195 SvmName: d.Get("svm_name").(string), 196 Name: d.Get("name").(string), 197 Size: &workenv.Capacity{ 198 Size: d.Get("size").(float64), 199 Unit: d.Get("size_unit").(string), 200 }, 201 ThinProvisioning: d.Get("thin_provisioning").(bool), 202 } 203 204 if attr, ok := d.GetOk("aggregate_name"); ok { 205 req.AggregateName = attr.(string) 206 } 207 208 if attrSize, okSize := d.GetOk("initial_size"); okSize { 209 if attrUnit, okUnit := d.GetOk("initial_size_unit"); okUnit { 210 req.InitialSize = &workenv.Capacity{ 211 Size: attrSize.(float64), 212 Unit: attrUnit.(string), 213 } 214 } 215 } 216 217 if attr, ok := d.GetOk("verify_name_uniqueness"); ok { 218 req.VerifyNameUniqueness = attr.(bool) 219 } 220 221 if attr, ok := d.GetOk("capacity_tier"); ok { 222 req.CapacityTier = attr.(string) 223 } 224 225 if attr, ok := d.GetOk("provider_volume_type"); ok { 226 req.ProviderVolumeType = attr.(string) 227 } 228 229 if attr, ok := d.GetOk("iops"); ok { 230 if d.Get("provider_volume_type").(string) != "io1" { 231 log.Printf("[INFO] IOPS only supported for io1 volume types, ignoring") 232 } else { 233 req.IOPS = attr.(int) 234 } 235 } 236 237 return &req 238 } 239 240 func buildVolumeCreateRequest(d *schema.ResourceData) *vsa.VSAVolumeCreateRequest { 241 req := vsa.VSAVolumeCreateRequest{ 242 WorkingEnvironmentId: d.Get("workenv_id").(string), 243 SvmName: d.Get("svm_name").(string), 244 AggregateName: d.Get("aggregate_name").(string), 245 Name: d.Get("name").(string), 246 Size: &workenv.Capacity{ 247 Size: d.Get("size").(float64), 248 Unit: d.Get("size_unit").(string), 249 }, 250 SnapshotPolicyName: d.Get("snapshot_policy").(string), 251 ThinProvisioning: d.Get("thin_provisioning").(bool), 252 Compression: d.Get("compression").(bool), 253 Deduplication: d.Get("deduplication").(bool), 254 } 255 256 if attrSize, okSize := d.GetOk("initial_size"); okSize { 257 if attrUnit, okUnit := d.GetOk("initial_size_unit"); okUnit { 258 req.InitialSize = &workenv.Capacity{ 259 Size: attrSize.(float64), 260 Unit: attrUnit.(string), 261 } 262 } 263 } 264 265 volumeType := d.Get("type").(string) 266 if volumeType == "nfs" { 267 req.ExportPolicyInfo = parsePolicyInfo(d) 268 } else { 269 req.ShareInfo = parseCreateShareInfoRequest(d) 270 } 271 272 if attr, ok := d.GetOk("capacity_tier"); ok { 273 req.CapacityTier = attr.(string) 274 } 275 276 if attr, ok := d.GetOk("provider_volume_type"); ok { 277 req.ProviderVolumeType = attr.(string) 278 } 279 280 if attr, ok := d.GetOk("iops"); ok { 281 if d.Get("provider_volume_type").(string) != "io1" { 282 log.Printf("[INFO] IOPS only supported for io1 volume types, ignoring") 283 } else { 284 req.IOPS = attr.(int) 285 } 286 } 287 288 if attr, ok := d.GetOkExists("max_num_disks_approved_to_add"); ok { 289 req.MaxNumOfDisksApprovedToAdd = attr.(int) 290 } 291 292 if attr, ok := d.GetOk("sync_to_s3"); ok { 293 req.SyncToS3 = attr.(bool) 294 } 295 296 return &req 297 } 298 299 func buildVolumeModifyRequest(d *schema.ResourceData) *workenv.VolumeModifyRequest { 300 req := workenv.VolumeModifyRequest{} 301 302 if d.HasChange("snapshot_policy") { 303 log.Printf("[DEBUG] Detected snapshot policy change") 304 req.SnapshotPolicyName = d.Get("snapshot_policy").(string) 305 } 306 307 volumeType := d.Get("type").(string) 308 if volumeType == "nfs" { 309 if d.HasChange("export_policy") { 310 log.Printf("[DEBUG] Detected export policy changes") 311 req.ExportPolicyInfo = parsePolicyInfo(d) 312 } 313 } else { 314 if d.HasChange("share") { 315 log.Printf("[DEBUG] Detected share changes") 316 req.ShareInfo = parseShareInfo(d) 317 } 318 } 319 320 return &req 321 } 322 323 func buildVolumeTierChangeRequest(d *schema.ResourceData) *workenv.ChangeVolumeTierRequest { 324 var createAggregateIfNotFound bool 325 if attr, ok := d.GetOkExists("create_aggregate_if_not_found"); ok { 326 createAggregateIfNotFound = attr.(bool) 327 } else { 328 createAggregateIfNotFound = true 329 } 330 331 req := workenv.ChangeVolumeTierRequest{ 332 AggregateName: d.Get("aggregate_name").(string), 333 NewAggregate: createAggregateIfNotFound, 334 } 335 336 if attr, ok := d.GetOkExists("max_num_disks_approved_to_add"); d.HasChange("max_num_disks_approved_to_add") && ok { 337 req.NumOfDisks = attr.(int) 338 } 339 340 if attr, ok := d.GetOk("provider_volume_type"); ok { 341 req.NewDiskTypeName = attr.(string) 342 } 343 344 if attr, ok := d.GetOk("iops"); ok { 345 if d.Get("provider_volume_type").(string) != "io1" { 346 log.Printf("[INFO] IOPS only supported for io1 volume types, ignoring") 347 } else { 348 req.IOPS = attr.(int) 349 } 350 } 351 352 if attr, ok := d.GetOk("capacity_tier"); d.HasChange("capacity_tier") && ok { 353 req.NewCapacityTier = attr.(string) 354 } 355 356 return &req 357 } 358 359 func parsePolicyInfo(d *schema.ResourceData) *workenv.ExportPolicyInfo { 360 policyInfo := workenv.ExportPolicyInfo{ 361 PolicyType: "none", 362 IPs: []string{}, 363 } 364 365 if exportPolicy, ok := d.GetOk("export_policy"); ok { 366 policyInfo.PolicyType = "custom" 367 368 for _, ip := range exportPolicy.([]interface{}) { 369 policyInfo.IPs = append(policyInfo.IPs, ip.(string)) 370 } 371 } 372 373 return &policyInfo 374 } 375 376 func parseCreateShareInfoRequest(d *schema.ResourceData) *workenv.CreateCIFSShareInfoRequest { 377 share := d.Get("share").([]interface{})[0].(map[string]interface{}) 378 p := share["permission"].([]interface{})[0].(map[string]interface{}) 379 380 users := []string{} 381 for _, user := range p["users"].([]interface{}) { 382 users = append(users, user.(string)) 383 } 384 385 permissions := workenv.CIFSShareUserPermissions{ 386 Permission: p["type"].(string), 387 Users: users, 388 } 389 390 shareInfo := workenv.CreateCIFSShareInfoRequest{ 391 ShareName: share["name"].(string), 392 AccessControl: permissions, 393 } 394 395 return &shareInfo 396 } 397 398 func parseShareInfo(d *schema.ResourceData) *workenv.CIFSShareInfo { 399 share := d.Get("share").([]interface{})[0].(map[string]interface{}) 400 401 permissions := []workenv.CIFSShareUserPermissions{} 402 for _, pp := range share["permission"].([]interface{}) { 403 p := pp.(map[string]interface{}) 404 405 users := []string{} 406 for _, user := range p["users"].([]interface{}) { 407 users = append(users, user.(string)) 408 } 409 410 permission := workenv.CIFSShareUserPermissions{ 411 Permission: p["type"].(string), 412 Users: users, 413 } 414 415 permissions = append(permissions, permission) 416 } 417 418 shareInfo := workenv.CIFSShareInfo{ 419 ShareName: share["name"].(string), 420 AccessControlList: permissions, 421 } 422 423 return &shareInfo 424 } 425 426 func processVolumeResourceData(d *schema.ResourceData, res *workenv.VolumeResponse, workenvId string) error { 427 d.Set("name", res.Name) 428 d.Set("svm_name", res.SvmName) 429 d.Set("workenv_id", workenvId) 430 if _, ok := d.GetOk("aggregate_name"); ok { 431 // only set the aggregate name if it is explicitly provided in config 432 d.Set("aggregate_name", res.AggregateName) 433 } 434 d.Set("size", res.Size.Size) 435 d.Set("size_unit", res.Size.Unit) 436 d.Set("thin_provisioning", res.ThinProvisioning) 437 d.Set("compression", res.Compression) 438 d.Set("deduplication", res.Deduplication) 439 d.Set("snapshot_policy", res.SnapshotPolicy) 440 d.Set("provider_volume_type", res.ProviderVolumeType) 441 d.Set("export_policy", flattenExportPolicy(res.ExportPolicyInfo)) 442 d.Set("share", flattenShareInfos(res.ShareInfo)) 443 if _, ok := d.GetOk("export_policy"); ok { 444 d.Set("type", "nfs") 445 } else { 446 d.Set("type", "cifs") 447 } 448 449 return nil 450 } 451 452 func flattenExportPolicy(policy *workenv.NamedExportPolicyInfo) []interface{} { 453 if policy.PolicyType != "custom" { 454 return nil 455 } 456 457 result := []interface{}{} 458 for _, ip := range policy.IPs { 459 result = append(result, ip) 460 } 461 return result 462 } 463 464 func flattenShareInfos(infos []workenv.CIFSShareInfo) []interface{} { 465 shares := []interface{}{} 466 467 for _, info := range infos { 468 shares = append(shares, flattenShareInfo(&info)) 469 } 470 471 return shares 472 } 473 474 func flattenShareInfo(info *workenv.CIFSShareInfo) map[string]interface{} { 475 permissions := []interface{}{} 476 for _, acl := range info.AccessControlList { 477 permission := map[string]interface{}{ 478 "type": acl.Permission, 479 "users": acl.Users, 480 } 481 permissions = append(permissions, permission) 482 } 483 484 share := map[string]interface{}{ 485 "name": info.ShareName, 486 "permission": permissions, 487 } 488 489 return share 490 } 491 492 func resourceCloudVolumeCreate(d *schema.ResourceData, meta interface{}) error { 493 apis := meta.(*APIs) 494 495 workenvId := d.Get("workenv_id").(string) 496 workenv, err := GetWorkingEnvironmentById(apis, workenvId) 497 if err != nil { 498 return err 499 } 500 501 // prepare volume quote first 502 quoteReq := buildVolumeQuoteRequest(d) 503 504 log.Printf("[DEBUG] Requesting a quote for volume %s", quoteReq.Name) 505 log.Printf("[DEBUG] Quote request: %s", util.ToString(quoteReq)) 506 507 // quote the volume creation 508 var quoteRes *vsa.VSAVolumeQuoteResponse 509 if workenv.IsHA { 510 quoteRes, err = apis.AWSHAWorkingEnvironmentAPI.QuoteVolume(quoteReq) 511 } else { 512 quoteRes, err = apis.VSAWorkingEnvironmentAPI.QuoteVolume(quoteReq) 513 } 514 515 if err != nil { 516 return err 517 } 518 519 log.Printf("[DEBUG] Quote response: %s", util.ToString(quoteRes)) 520 521 // if the aggregate creation is not defined, use the one returned by the quote response 522 // NOTE: this is temporarily broken as providing the value of "false" will 523 // use the value provided in the quote response - this is due to the way 524 // the GetOk method works, there is no way to differentiate between not having 525 // the value and the value being set to "false" 526 var createAggregateIfNotFound bool 527 if attr, ok := d.GetOkExists("create_aggregate_if_not_found"); ok { 528 createAggregateIfNotFound = attr.(bool) 529 } else { 530 createAggregateIfNotFound = quoteRes.NewAggregate 531 } 532 533 log.Printf("[INFO] Creating volume %s", quoteReq.Name) 534 535 // create the actual volume request 536 req := buildVolumeCreateRequest(d) 537 538 // set the request aggregate 539 if attr, ok := d.GetOk("aggregate_name"); ok { 540 req.AggregateName = attr.(string) 541 } else { 542 req.AggregateName = quoteRes.AggregateName 543 } 544 545 // if no max volumes was provided, use one from the quote response 546 if _, ok := d.GetOkExists("max_num_disks_approved_to_add"); !ok { 547 req.MaxNumOfDisksApprovedToAdd = quoteRes.NumOfDisks 548 } 549 550 log.Printf("[DEBUG] Setting create aggregate if not found flag to %t", createAggregateIfNotFound) 551 log.Printf("[DEBUG] Request: %s", util.ToString(req)) 552 553 // actual the volume creation 554 var requestId string 555 if workenv.IsHA { 556 requestId, err = apis.AWSHAWorkingEnvironmentAPI.CreateVolume(createAggregateIfNotFound, req) 557 } else { 558 requestId, err = apis.VSAWorkingEnvironmentAPI.CreateVolume(createAggregateIfNotFound, req) 559 } 560 561 if err != nil { 562 return err 563 } 564 565 if err = WaitForRequest(apis, requestId); err != nil { 566 return err 567 } 568 569 log.Printf("[INFO] Volume %s created successfully", quoteReq.Name) 570 571 // set the ID 572 volType := "vsa" 573 if workenv.IsHA { 574 volType = "ha" 575 } 576 d.SetId(fmt.Sprintf("%s/%s/%s/%s", volType, workenvId, workenv.SvmName, req.Name)) 577 578 return resourceCloudVolumeRead(d, meta) 579 } 580 581 func resourceCloudVolumeRead(d *schema.ResourceData, meta interface{}) error { 582 apis := meta.(*APIs) 583 584 volumeType, workenvId, _, volumeName, isHA, err := splitId(d.Id()) 585 if err != nil { 586 return err 587 } 588 589 log.Printf("[INFO] Reading %s volume %s for work env %s", strings.ToUpper(volumeType), volumeName, workenvId) 590 591 // get volume data 592 var res *workenv.VolumeResponse 593 if isHA { 594 res, err = apis.AWSHAWorkingEnvironmentAPI.GetVolume(workenvId, volumeName) 595 } else { 596 res, err = apis.VSAWorkingEnvironmentAPI.GetVolume(workenvId, volumeName) 597 } 598 599 if err != nil { 600 return err 601 } 602 603 log.Printf("[DEBUG] Response: %s", util.ToString(res)) 604 log.Printf("[DEBUG] Processing volume %s", volumeName) 605 606 // set volume details in resource data 607 processVolumeResourceData(d, res, workenvId) 608 609 log.Printf("[INFO] Volume %s read successfully", volumeName) 610 611 return nil 612 } 613 614 func resourceCloudVolumeUpdate(d *schema.ResourceData, meta interface{}) error { 615 apis := meta.(*APIs) 616 617 // NOTE: the create_aggregate_if_not_found attribute is not supported by the 618 // underlying NetApp API and therefore not used in the update call 619 620 volumeType, workenvId, svmName, volumeName, isHA, err := splitId(d.Id()) 621 if err != nil { 622 return err 623 } 624 625 log.Printf("[INFO] Updating %s volume %s for work env %s", strings.ToUpper(volumeType), volumeName, workenvId) 626 627 // check for data that can be updated on the volume 628 volumeDataChanged := false 629 if d.HasChange("snapshot_policy") || d.HasChange("export_policy") || d.HasChange("share") { 630 volumeDataChanged = true 631 } 632 633 // check if the volume tier has changed 634 volumeTierChanged := false 635 if d.HasChange("aggregate_name") || d.HasChange("provider_volume_type") || d.HasChange("capacity_tier") { 636 volumeTierChanged = true 637 } 638 639 if volumeDataChanged { 640 err = updateVolumeData(d, apis, volumeType, workenvId, svmName, volumeName, isHA) 641 if err != nil { 642 return err 643 } 644 645 d.SetPartial("snapshot_policy") 646 d.SetPartial("export_policy") 647 d.SetPartial("share") 648 } 649 650 if volumeTierChanged { 651 err = updateVolumeTier(d, meta, apis, volumeType, workenvId, svmName, volumeName, isHA) 652 if err != nil { 653 return err 654 } 655 656 d.SetPartial("aggregate_name") 657 d.SetPartial("provider_volume_type") 658 d.SetPartial("capacity_tier") 659 } 660 661 d.Partial(false) 662 663 log.Printf("[INFO] Volume %s updated successfully", volumeName) 664 665 return nil 666 } 667 668 func resourceCloudVolumeDelete(d *schema.ResourceData, meta interface{}) error { 669 apis := meta.(*APIs) 670 671 volumeType, workenvId, svmName, volumeName, isHA, err := splitId(d.Id()) 672 if err != nil { 673 return err 674 } 675 676 log.Printf("[INFO] Deleting %s volume %s for work env %s", strings.ToUpper(volumeType), volumeName, workenvId) 677 678 var requestId string 679 if isHA { 680 requestId, err = apis.AWSHAWorkingEnvironmentAPI.DeleteVolume(workenvId, svmName, volumeName) 681 } else { 682 requestId, err = apis.VSAWorkingEnvironmentAPI.DeleteVolume(workenvId, svmName, volumeName) 683 } 684 685 if err != nil { 686 return err 687 } 688 689 if err = WaitForRequest(apis, requestId); err != nil { 690 return err 691 } 692 693 log.Printf("[INFO] Volume %s deleted successfully", volumeName) 694 695 d.SetId("") 696 697 return nil 698 } 699 700 func updateVolumeData(d *schema.ResourceData, apis *APIs, volumeType, workenvId, svmName, volumeName string, isHA bool) error { 701 req := buildVolumeModifyRequest(d) 702 703 log.Printf("[INFO] Modifying %s volume %s for work env %s", strings.ToUpper(volumeType), volumeName, workenvId) 704 log.Printf("[DEBUG] Request: %s", util.ToString(req)) 705 706 var err error 707 var requestId string 708 if isHA { 709 requestId, err = apis.AWSHAWorkingEnvironmentAPI.ModifyVolume(workenvId, svmName, volumeName, req) 710 } else { 711 requestId, err = apis.VSAWorkingEnvironmentAPI.ModifyVolume(workenvId, svmName, volumeName, req) 712 } 713 714 if err != nil { 715 return err 716 } 717 718 if err = WaitForRequest(apis, requestId); err != nil { 719 return err 720 } 721 722 log.Printf("[INFO] Volume %s modified successfully", volumeName) 723 724 return nil 725 } 726 727 func updateVolumeTier(d *schema.ResourceData, meta interface{}, apis *APIs, volumeType, workenvId, svmName, volumeName string, isHA bool) error { 728 log.Printf("[INFO] Modifying tier for %s volume %s for work env %s", strings.ToUpper(volumeType), volumeName, workenvId) 729 730 // prepare volume quote first 731 quoteReq := buildVolumeQuoteRequest(d) 732 733 log.Printf("[DEBUG] Requesting tier change quote for volume %s", quoteReq.Name) 734 log.Printf("[DEBUG] Quote request: %s", util.ToString(quoteReq)) 735 736 // quote the volume creation 737 var err error 738 var quoteRes *vsa.VSAVolumeQuoteResponse 739 if isHA { 740 quoteRes, err = apis.AWSHAWorkingEnvironmentAPI.QuoteVolume(quoteReq) 741 } else { 742 quoteRes, err = apis.VSAWorkingEnvironmentAPI.QuoteVolume(quoteReq) 743 } 744 745 if err != nil { 746 return err 747 } 748 749 log.Printf("[DEBUG] Quote response: %s", util.ToString(quoteRes)) 750 751 req := buildVolumeTierChangeRequest(d) 752 753 // set the request aggregate 754 if attr, ok := d.GetOk("aggregate_name"); ok { 755 req.AggregateName = attr.(string) 756 } else { 757 req.AggregateName = quoteRes.AggregateName 758 } 759 760 // if the aggregate control flag is not set, use the quote response one 761 if attr, ok := d.GetOkExists("create_aggregate_if_not_found"); ok { 762 req.NewAggregate = attr.(bool) 763 } else { 764 req.NewAggregate = quoteRes.NewAggregate 765 } 766 767 // if no max volumes was provided, use one from the quote response 768 if _, ok := d.GetOkExists("max_num_disks_approved_to_add"); !ok { 769 req.NumOfDisks = quoteRes.NumOfDisks 770 } 771 772 log.Printf("[DEBUG] Request: %s", util.ToString(req)) 773 774 var requestId string 775 if isHA { 776 requestId, err = apis.AWSHAWorkingEnvironmentAPI.ChangeVolumeTier(workenvId, svmName, volumeName, req) 777 } else { 778 requestId, err = apis.VSAWorkingEnvironmentAPI.ChangeVolumeTier(workenvId, svmName, volumeName, req) 779 } 780 781 if err != nil { 782 return err 783 } 784 785 if err = WaitForRequest(apis, requestId); err != nil { 786 return err 787 } 788 789 log.Printf("[INFO] Tier for volume %s modified successfully", volumeName) 790 791 return resourceCloudVolumeRead(d, meta) 792 } 793 794 func splitId(id string) (string, string, string, string, bool, error) { 795 parts := strings.Split(id, "/") 796 if len(parts) != 4 { 797 return "", "", "", "", false, fmt.Errorf("Invalid volume ID format: %s", id) 798 } 799 800 volumeType := parts[0] 801 workenvId := parts[1] 802 svmName := parts[2] 803 volumeName := parts[3] 804 isHA := false 805 if volumeType == "ha" { 806 isHA = true 807 } 808 809 return volumeType, workenvId, svmName, volumeName, isHA, nil 810 }