Skip to content

Commit 2401828

Browse files
committed
Sync bitbucket and GitHub
1 parent 4aa5aae commit 2401828

10 files changed

Lines changed: 206 additions & 17 deletions

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
## 27.2.0
2+
3+
NEW FEATURES:
4+
* resource/cvo_gcp: Added support for `iops` and `throughput` parameters during GCP CVO creation with `hyperdisk-balanced` volume type.
5+
* resource/aggregate: Added support for `iops` and `throughput` parameters during aggregate creation with `hyperdisk-balanced` provider volume type.
6+
* resource/aggregate: Added support for in-place update of `iops` and `throughput` on existing `hyperdisk-balanced` aggregates without requiring resource recreation.
7+
8+
ENHANCEMENTS:
9+
* resource/aggregate: Added `Computed` attribute for `iops` and `throughput` to support import and drift detection by reading actual values from the API.
10+
* resource/aggregate: Added conditional `ForceNew` via `CustomizeDiff` for `iops` and `throughput` — in-place update for `hyperdisk-balanced`, destroy+recreate for other volume types (backward compatible).
11+
* resource/aggregate: Updated import to populate `iops` and `throughput` from the API response.
12+
113
## 27.1.0
214

315
NEW FEATURES:

cloudmanager/aggregate.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ type increaseAggregateCapacityRequest struct {
116116
CapacityToAdd diskSize `structs:"capacityToAdd"`
117117
}
118118

119+
type updateAggregateIopsThroughputRequest struct {
120+
WorkingEnvironmentID string `structs:"workingEnvironmentId"`
121+
Name string `structs:"name"`
122+
Iops int `structs:"iops,omitempty"`
123+
Throughput int `structs:"throughput,omitempty"`
124+
}
125+
119126
// get aggregate by workingEnvironmentId+aggregate name
120127
func (c *Client) getAggregate(request aggregateRequest, name string, sourceWorkingEnvironmentType string, clientID string, isSaaS bool, connectorIP string) (aggregateResult, error) {
121128
log.Printf("getAggregate %s", name)
@@ -367,6 +374,45 @@ func (c *Client) increaseAggregateCapacity(request increaseAggregateCapacityRequ
367374
return err
368375
}
369376

377+
func (c *Client) updateAggregateIopsThroughput(request updateAggregateIopsThroughputRequest, clientID string, isSaaS bool, connectorIP string) error {
378+
log.Printf("updateAggregateIopsThroughput for aggregate %s (iops: %d, throughput: %d)", request.Name, request.Iops, request.Throughput)
379+
380+
params := structs.Map(request)
381+
hostType := "CloudManagerHost"
382+
if !isSaaS {
383+
hostType = "http://" + connectorIP
384+
}
385+
386+
var baseURL string
387+
rootURL, _, err := c.getAPIRoot(request.WorkingEnvironmentID, clientID, isSaaS, connectorIP)
388+
if err != nil {
389+
log.Print("updateAggregateIopsThroughput: Cannot get API root.")
390+
return err
391+
}
392+
393+
baseURL = fmt.Sprintf("%s/aggregates/%s/%s", rootURL, request.WorkingEnvironmentID, request.Name)
394+
395+
statusCode, response, onCloudRequestID, err := c.CallAPIMethod("PUT", baseURL, params, c.Token, hostType, clientID)
396+
if err != nil {
397+
log.Print("updateAggregateIopsThroughput request failed")
398+
return err
399+
}
400+
401+
responseError := apiResponseChecker(statusCode, response, "updateAggregateIopsThroughput")
402+
if responseError != nil {
403+
return responseError
404+
}
405+
406+
log.Print("Wait for aggregate IOPS/throughput update.")
407+
if isSaaS {
408+
err = c.waitOnCompletion(onCloudRequestID, "Aggregate", "update IOPS/throughput", 15, 60, clientID)
409+
} else {
410+
err = c.waitOnCompletionForNotSaas(onCloudRequestID, "Aggregate", "update IOPS/throughput", 15, 60, clientID, connectorIP)
411+
}
412+
413+
return err
414+
}
415+
370416
// flattenCapacity: convert struct size + unit
371417
func flattenCapacity(c capacity) interface{} {
372418
flattened := make(map[string]interface{})

cloudmanager/cvo_gcp.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type createCVOGCPDetails struct {
2828
VsaMetadata vsaMetadata `structs:"vsaMetadata"`
2929
GCPVolumeSize diskSize `structs:"gcpVolumeSize"`
3030
GCPVolumeType string `structs:"gcpVolumeType"`
31+
GcpPerformance gcpPerformance `structs:"gcpPerformance,omitempty"`
3132
SubnetID string `structs:"subnetId"`
3233
SubnetPath string `structs:"subnetPath"`
3334
Project string `structs:"project"`
@@ -56,6 +57,12 @@ type gcpLabels struct {
5657
LabelValue string `structs:"labelValue,omitempty"`
5758
}
5859

60+
// gcpPerformance for setting IOPS and throughput on hyperdisk-balanced
61+
type gcpPerformance struct {
62+
Iops int `structs:"iops,omitempty"`
63+
Throughput int `structs:"throughput,omitempty"`
64+
}
65+
5966
// gcpSVMs the input for adding SVMs to a CVO HA
6067
type gcpSVM struct {
6168
SvmName string `structs:"svmName"`

cloudmanager/resource_netapp_cloudmanager_aggregate.go

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,16 @@ func resourceAggregate() *schema.Resource {
7575
ValidateFunc: validation.StringInSlice([]string{"NONE", "S3", "Blob", "cloudStorage"}, true),
7676
},
7777
"iops": {
78-
Type: schema.TypeInt,
79-
Optional: true,
80-
ForceNew: true,
78+
Type: schema.TypeInt,
79+
Optional: true,
80+
Computed: true,
81+
Description: "Provisioned IOPS. Applicable when provider_volume_type is 'io1', 'gp3', or 'hyperdisk-balanced'. For 'hyperdisk-balanced', valid range is 3000-160000.",
8182
},
8283
"throughput": {
83-
Type: schema.TypeInt,
84-
Optional: true,
85-
ForceNew: true,
84+
Type: schema.TypeInt,
85+
Optional: true,
86+
Computed: true,
87+
Description: "Provisioned throughput in MBps. Applicable when provider_volume_type is 'gp3' or 'hyperdisk-balanced'. For 'hyperdisk-balanced', valid range is 140-2400.",
8688
},
8789
"connector_ip": {
8890
Type: schema.TypeString,
@@ -215,6 +217,18 @@ func resourceAggregateCreate(d *schema.ResourceData, meta interface{}) error {
215217
log.Printf("CreateAggregate: provider_volume_type is gp3, but throughput is not configured.")
216218
}
217219
}
220+
if aggregate.ProviderVolumeType == "hyperdisk-balanced" {
221+
if a, ok := d.GetOk("iops"); ok {
222+
aggregate.Iops = a.(int)
223+
} else {
224+
log.Printf("CreateAggregate: provider_volume_type is hyperdisk-balanced, but iops is not configured.")
225+
}
226+
if a, ok := d.GetOk("throughput"); ok {
227+
aggregate.Throughput = a.(int)
228+
} else {
229+
log.Printf("CreateAggregate: provider_volume_type is hyperdisk-balanced, but throughput is not configured.")
230+
}
231+
}
218232
}
219233
if a, ok := d.GetOk("capacity_tier"); ok {
220234
if a.(string) != "NONE" {
@@ -299,9 +313,13 @@ func resourceAggregateRead(d *schema.ResourceData, meta interface{}) error {
299313
d.SetId(aggr.Name)
300314
d.Set("number_of_disks", len(aggr.Disks))
301315
d.Set("working_environment_name", workingEnv.Name)
302-
d.Set("disk_size_size", aggr.ProviderVolumes[0].Size.Size)
303-
d.Set("disk_size_unit", aggr.ProviderVolumes[0].Size.Unit)
304-
d.Set("provider_volume_type", aggr.ProviderVolumes[0].DiskType)
316+
if len(aggr.ProviderVolumes) > 0 {
317+
d.Set("disk_size_size", aggr.ProviderVolumes[0].Size.Size)
318+
d.Set("disk_size_unit", aggr.ProviderVolumes[0].Size.Unit)
319+
d.Set("provider_volume_type", aggr.ProviderVolumes[0].DiskType)
320+
d.Set("iops", aggr.ProviderVolumes[0].Iops)
321+
d.Set("throughput", aggr.ProviderVolumes[0].Throughput)
322+
}
305323
}
306324

307325
if aggr.Name != d.Get("name").(string) {
@@ -313,6 +331,10 @@ func resourceAggregateRead(d *schema.ResourceData, meta interface{}) error {
313331
d.Set("total_capacity_unit", aggr.TotalCapacity.Unit)
314332
d.Set("available_capacity_size", aggr.AvailableCapacity.Size)
315333
d.Set("available_capacity_unit", aggr.AvailableCapacity.Unit)
334+
if len(aggr.ProviderVolumes) > 0 {
335+
d.Set("iops", aggr.ProviderVolumes[0].Iops)
336+
d.Set("throughput", aggr.ProviderVolumes[0].Throughput)
337+
}
316338

317339
return nil
318340
}
@@ -413,6 +435,26 @@ func resourceAggregateUpdate(d *schema.ResourceData, meta interface{}) error {
413435
}
414436
}
415437

438+
// Handle IOPS/Throughput update (hyperdisk-balanced only, enforced by CustomizeDiff)
439+
if d.HasChange("iops") || d.HasChange("throughput") {
440+
updateRequest := updateAggregateIopsThroughputRequest{
441+
WorkingEnvironmentID: workingEnvDetail.PublicID,
442+
Name: request.Name,
443+
}
444+
if d.HasChange("iops") {
445+
updateRequest.Iops = d.Get("iops").(int)
446+
}
447+
if d.HasChange("throughput") {
448+
updateRequest.Throughput = d.Get("throughput").(int)
449+
}
450+
451+
err := client.updateAggregateIopsThroughput(updateRequest, clientID, isSaaS, connectorIP)
452+
if err != nil {
453+
return fmt.Errorf("failed to update aggregate IOPS/throughput: %v", err)
454+
}
455+
log.Printf("Successfully updated IOPS/throughput for aggregate %s", request.Name)
456+
}
457+
416458
// Handle disk count update
417459
if d.HasChange("number_of_disks") {
418460
expectNumber := d.Get("number_of_disks")
@@ -536,6 +578,18 @@ func resourceAggregateCustomizeDiff(diff *schema.ResourceDiff, v interface{}) er
536578
return fmt.Errorf("increase_capacity_size is required when increase_capacity_unit is specified")
537579
}
538580

581+
// For non-hyperdisk-balanced volume types, force replacement on iops/throughput changes
582+
// to preserve backward compatibility (gp3, io1, etc. previously had ForceNew: true)
583+
providerVolumeType := diff.Get("provider_volume_type").(string)
584+
if providerVolumeType != "hyperdisk-balanced" {
585+
if diff.HasChange("iops") {
586+
diff.ForceNew("iops")
587+
}
588+
if diff.HasChange("throughput") {
589+
diff.ForceNew("throughput")
590+
}
591+
}
592+
539593
return nil
540594
}
541595

cloudmanager/resource_netapp_cloudmanager_cvo_gcp.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func resourceCVOGCP() *schema.Resource {
5959
Optional: true,
6060
ForceNew: true,
6161
Default: "pd-ssd",
62-
ValidateFunc: validation.StringInSlice([]string{"pd-balanced", "pd-standard", "pd-ssd"}, false),
62+
ValidateFunc: validation.StringInSlice([]string{"pd-balanced", "pd-standard", "pd-ssd", "hyperdisk-balanced"}, false),
6363
},
6464
"gcp_volume_size": {
6565
Type: schema.TypeInt,
@@ -74,6 +74,18 @@ func resourceCVOGCP() *schema.Resource {
7474
Default: "TB",
7575
ValidateFunc: validation.StringInSlice([]string{"GB", "TB"}, false),
7676
},
77+
"iops": {
78+
Type: schema.TypeInt,
79+
Optional: true,
80+
ForceNew: true,
81+
Description: "Provisioned IOPS for the initial aggregate. Only applicable when gcp_volume_type is 'hyperdisk-balanced'. Valid range is 3000-160000. To update IOPS after creation, use the netapp-cloudmanager_aggregate resource.",
82+
},
83+
"throughput": {
84+
Type: schema.TypeInt,
85+
Optional: true,
86+
ForceNew: true,
87+
Description: "Provisioned throughput for the initial aggregate. Only applicable when gcp_volume_type is 'hyperdisk-balanced'. Valid range is 140-2400. To update throughput after creation, use the netapp-cloudmanager_aggregate resource.",
88+
},
7789
"ontap_version": {
7890
Type: schema.TypeString,
7991
Optional: true,
@@ -416,6 +428,14 @@ func resourceCVOGCPCreate(d *schema.ResourceData, meta interface{}) error {
416428
cvoDetails.DataEncryptionType = d.Get("data_encryption_type").(string)
417429
cvoDetails.WorkspaceID = d.Get("workspace_id").(string)
418430
cvoDetails.GCPVolumeType = d.Get("gcp_volume_type").(string)
431+
if cvoDetails.GCPVolumeType == "hyperdisk-balanced" {
432+
if c, ok := d.GetOk("iops"); ok {
433+
cvoDetails.GcpPerformance.Iops = c.(int)
434+
}
435+
if c, ok := d.GetOk("throughput"); ok {
436+
cvoDetails.GcpPerformance.Throughput = c.(int)
437+
}
438+
}
419439
cvoDetails.SvmPassword = d.Get("svm_password").(string)
420440
if c, ok := d.GetOk("svm_name"); ok {
421441
cvoDetails.SvmName = c.(string)
@@ -967,6 +987,14 @@ func resourceCVOGCPCustomizeDiff(diff *schema.ResourceDiff, v interface{}) error
967987
}
968988
}
969989

990+
//Validate iops/throughput only allowed for hyperdisk-balanced
991+
gcpVolumeType := diff.Get("gcp_volume_type").(string)
992+
_, hasIops := diff.GetOk("iops")
993+
_, hasThroughput := diff.GetOk("throughput")
994+
if (hasIops || hasThroughput) && gcpVolumeType != "hyperdisk-balanced" {
995+
return fmt.Errorf("performance parameters (iops, throughput) can only be specified when gcp_volume_type is 'hyperdisk-balanced', current type is '%s'", gcpVolumeType)
996+
}
997+
970998
// WORM validation
971999
wormLength, lengthOk := diff.GetOk("worm_retention_period_length")
9721000
wormUnit, unitOk := diff.GetOk("worm_retention_period_unit")

website/docs/d/volume.html.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ The following attributes are exported in addition to the arguments listed above:
4141
* `svm_name` - The name of the SVM.
4242
* `size` - The volume size, supported with decimal numbers.
4343
* `size_unit` - ['Byte' or 'KB' or 'MB' or 'GB' or 'TB'].
44-
* `provider_volume_type` - The underlying cloud provider volume type. For AWS: ['gp3', 'gp2', 'io1', 'st1', 'sc1']. For Azure: ['Premium_LRS','Standard_LRS','StandardSSD_LRS']. For GCP: ['pd-balanced', 'pd-ssd','pd-standard']
44+
* `provider_volume_type` - The underlying cloud provider volume type. For AWS: ['gp3', 'gp2', 'io1', 'st1', 'sc1']. For Azure: ['Premium_LRS','Standard_LRS','StandardSSD_LRS']. For GCP: ['pd-balanced', 'pd-ssd','pd-standard', 'hyperdisk-balanced']
4545
* `enable_thin_provisioning` - Enable thin provisioning. The default is 'true'.
4646
* `enable_compression` - Enable compression. The default is 'true'.
4747
* `enable_deduplication` - Enable deduplication. The default is 'true'.

website/docs/r/aggregate.html.markdown

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,24 @@ resource "netapp-cloudmanager_aggregate" "cl-aggregate-with-capacity" {
6767
increase_capacity_unit = "GB"
6868
}
6969
```
70+
**Create netapp-cloudmanager_aggregate with GCP hyperdisk-balanced and custom IOPS/Throughput (C3 instance only) :**
71+
72+
```
73+
resource "netapp-cloudmanager_aggregate" "gcp-hyperdisk-aggr" {
74+
provider = netapp-cloudmanager
75+
name = "aggr_hyperdisk"
76+
working_environment_id = netapp-cloudmanager_cvo_gcp.cvo-gcp.id
77+
client_id = netapp-cloudmanager_connector_gcp.cm-gcp.client_id
78+
number_of_disks = 1
79+
provider_volume_type = "hyperdisk-balanced"
80+
disk_size_size = 100
81+
disk_size_unit = "GB"
82+
83+
# IOPS and throughput configuration
84+
iops = 5000
85+
throughput = 500
86+
}
87+
```
7088

7189
## Argument Reference
7290

@@ -85,10 +103,10 @@ The following arguments are supported:
85103
* `disk_size_size` - (Optional, Forces new resource) The required size of the disks. The max number depends on the `provider_volume_type`. Details in this document: AWS: [https://docs.netapp.com/us-en/cloud-volumes-ontap-relnotes/reference-limits-aws.html#aggregate-limits] Azure: [https://docs.netapp.com/us-en/cloud-volumes-ontap-relnotes/reference-limits-azure.html#aggregate-limits] GCP: [https://docs.netapp.com/us-en/cloud-volumes-ontap-relnotes/reference-limits-gcp.html#disk-and-tiering-limits] **Note: Must be provided together with `disk_size_unit`**
86104
* `disk_size_unit` - (Optional, Forces new resource) The disk size unit ['GB' or 'TB']. **Note: Must be provided together with `disk_size_size`**
87105
* `home_node` - (Optional, Forces new resource) The home node that the new aggregate should belong to. The default is the first node.
88-
* `provider_volume_type` - (Optional, Forces new resource) The cloud provider volume type. For AWS: ['gp3', 'gp2', 'io1', 'st1', 'sc1']. For Azure: ['Premium_LRS','Standard_LRS','StandardSSD_LRS']. For GCP: ['pd-balanced', 'pd-ssd','pd-standard']
106+
* `provider_volume_type` - (Optional, Forces new resource) The cloud provider volume type. For AWS: ['gp3', 'gp2', 'io1', 'st1', 'sc1']. For Azure: ['Premium_LRS','Standard_LRS','StandardSSD_LRS']. For GCP: ['pd-balanced', 'pd-ssd','pd-standard', 'hyperdisk-balanced']
89107
* `capacity_tier` - (Optional, Forces new resource) The aggregate's capacity tier for tiering cold data to object storage: ['S3', 'Blob', 'cloudStorage']. The default values for each cloud provider are as follows: Amazon => 'S3', Azure => 'Blob', GCP => 'cloudStorage'. If NONE, the capacity tier won't be set on aggregate creation.
90-
* `iops` - (Optional, Forces new resource) Provisioned IOPS. Needed only when 'providerVolumeType' is 'io1' or 'gp3'
91-
* `throughput` - (Optional, Forces new resource) Required only when 'providerVolumeType' is 'gp3'.
108+
* `iops` - (Optional) Provisioned IOPS. Applicable when 'providerVolumeType' is 'io1', 'gp3', or 'hyperdisk-balanced'. For 'hyperdisk-balanced', valid range is 3000-160000. Can be updated in-place for 'hyperdisk-balanced'; for other disk types, changing this value requires resource recreation.
109+
* `throughput` - (Optional) Provisioned throughput in MBps. Applicable when 'providerVolumeType' is 'gp3' or 'hyperdisk-balanced'. For 'hyperdisk-balanced', valid range is 140-2400. Can be updated in-place for 'hyperdisk-balanced'; for other disk types, changing this value requires resource recreation.
92110
* `initial_ev_aggregate_size` - (Optional, Forces new resource) Initial size for EBS Elastic Volumes aggregate (AWS only). This enables the aggregate to support capacity expansion using Amazon EBS Elastic Volumes. **Creation time only** - cannot be modified after aggregate creation. **Note: Must be provided together with `initial_ev_aggregate_unit`**
93111
* `initial_ev_aggregate_unit` - (Optional, Forces new resource) Unit for initial EBS Elastic Volumes aggregate size (GB, TB, GiB, or TiB). Only used with `initial_ev_aggregate_size`. Defaults to 'GB' if not specified. **Creation time only** - cannot be modified after aggregate creation. **Note: Must be provided together with `initial_ev_aggregate_size`**
94112
* `increase_capacity_size` - (Optional, Computed) Additional capacity to add to the aggregate using Amazon EBS Elastic Volumes. **Only supported for AWS aggregates with EBS Elastic Volumes enabled**. **Update operation only** - cannot be used during aggregate creation. The aggregate must be created with `initial_ev_aggregate_size` to support capacity increases. **Important:** After a successful capacity increase operation, remove the parameter from your configuration to prevent unnecessary state changes and achieve idempotency in subsequent Terraform runs. **Note: Must be provided together with `increase_capacity_unit`**

0 commit comments

Comments
 (0)