Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

41 enhancement netapp ontap storage volume need to support import #91

4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

## 1.1.0 ()
ENHANCEMENTS:
* **netapp-ontap_storage_volume_resource**: Add support for import ([#72](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/41))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this need to be 41


## 1.0.1 ()

BUG FIXES:
Expand Down
84 changes: 83 additions & 1 deletion docs/resources/storage_volume_resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,86 @@ Optional:
- `policy_name` (String) The tiering policy that is to be associated with the volume

## Import
Import is currently not support for this Resource.
This resource supports import, which allows you to import existing volumes into the state of this resource.
Import require a unique ID composed of the volume name, the volume name, separated by a comma.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the volume name, the volume name" ?


id = `name`,`svm_name`,`cx_profile_name`

### Terraform Import

For example
```shell
terraform import netapp-ontap_storage_volume_resource.example vol1,svm2,cluster5
```
!> The terraform import CLI command can only import resources into the state. Importing via the CLI does not generate configuration. If you want to generate the accompanying configuration for imported resources, use the import block instead.

### Terrafomr Import Block
This requires Terraform 1.5 or higher, and will auto create the configuration for you

First create the block
```terraform
import {
to = netapp-ontap_storage_volume_resource.volume_import
id = "svm1_root,svm1,cluster4"
}
```
Next run, this will auto create the configuration for you
```shell
terraform plan -generate-config-out=generated.tf
```
This will generate a file called generated.tf, which will contain the configuration for the imported resource
```terraform
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.

# __generated__ by Terraform from "svm1_root,svm1,cluster4"
resource "netapp-ontap_storage_volume_resource" "volume_import" {
aggregates = [
{
name = "aggr1"
},
]
analytics = {
state = "off"
}
comment = null
cx_profile_name = "cluster4"
efficiency = {
compression = "none"
policy_name = "-"
}
encryption = false
language = "c.utf_8"
name = "svm1_root"
nas = {
export_policy_name = "default"
group_id = 0
junction_path = "/"
security_style = "unix"
unix_permissions = 755
user_id = 0
}
qos_policy_group = null
snaplock = {
type = "non_snaplock"
}
snapshot_policy = "default"
space = {
logical_space = {
enforcement = false
reporting = false
}
percent_snapshot_space = 5
size = 20
size_unit = "mb"
}
space_guarantee = "volume"
state = "online"
svm_name = "svm1"
tiering = {
minimum_cooling_days = 0
policy_name = "none"
}
type = "rw"
}
```
65 changes: 51 additions & 14 deletions internal/provider/storage_volume_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/mitchellh/mapstructure"
"strings"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
Expand Down Expand Up @@ -382,12 +383,21 @@
// error reporting done inside NewClient
return
}
// Import don't have id's so we need to get the id from the name
var response *interfaces.StorageVolumeGetDataModelONTAP
if data.ID.ValueString() == "" {
response, err = interfaces.GetStorageVolumeByName(errorHandler, *client, data.Name.ValueString(), data.SVMName.ValueString())
data.ID = types.StringValue(response.UUID)
Fixed Show fixed Hide fixed
} else {
response, err = interfaces.GetStorageVolume(errorHandler, *client, data.ID.ValueString())
}

response, err := interfaces.GetStorageVolume(errorHandler, *client, data.ID.ValueString())
if err != nil {
return
}

tflog.Debug(ctx, fmt.Sprintf("read a volume resource: %#v", data))

data.Comment = types.StringValue(response.Comment)
data.Encrypt = types.BoolValue(response.Encryption.Enabled)
data.State = types.StringValue(response.State)
Expand All @@ -414,20 +424,26 @@
"logical_space": types.ObjectType{AttrTypes: nestedElementTypes},
}
var sizeUnit string
var size int64
var space StorageVolumeResourceSpace
diags := data.Space.As(ctx, &space, basetypes.ObjectAsOptions{})
if diags.HasError() {
resp.Diagnostics.Append(diags...)
return
}
if _, ok := interfaces.POW2BYTEMAP[space.SizeUnit.ValueString()]; !ok {
errorHandler.MakeAndReportError("error creating volume", fmt.Sprintf("invalid input for size_unit: %s, required one of: bytes, b, kb, mb, gb, tb, pb, eb, zb, yb", space.SizeUnit.ValueString()))
return
}
sizeUnit = space.SizeUnit.ValueString()
tflog.Debug(ctx, fmt.Sprintf("read a volume Space: %#v", space))
tflog.Debug(ctx, fmt.Sprintf("read a volume data.space: %#v", data.Space))
//diags := data.Space.As(ctx, &space, basetypes.ObjectAsOptions{})
//if diags.HasError() {
// resp.Diagnostics.Append(diags...)
// return
//}
tflog.Debug(ctx, fmt.Sprintf("read a volume Space3: %#v", space))
size, sizeUnit = interfaces.ByteFormat(int64(response.Space.Size))
//if _, ok := interfaces.POW2BYTEMAP[space.SizeUnit.ValueString()]; !ok {
// errorHandler.MakeAndReportError("error creating volume", fmt.Sprintf("invalid input for size_unit: %s, required one of: bytes, b, kb, mb, gb, tb, pb, eb, zb, yb", space.SizeUnit.ValueString()))
// return
//}
tflog.Debug(ctx, fmt.Sprintf("read a volume Space size: %#v", space))
//sizeUnit = space.SizeUnit.ValueString()

elements := map[string]attr.Value{
"size": types.Int64Value(int64(response.Space.Size / interfaces.POW2BYTEMAP[sizeUnit])),
"size": types.Int64Value(size),
"size_unit": types.StringValue(sizeUnit),
"percent_snapshot_space": types.Int64Value(int64(response.Space.Snapshot.ReservePercent)),
"logical_space": logicalObjectValue,
Expand Down Expand Up @@ -518,6 +534,15 @@
}
data.Analytics = objectValue

//Aggregates
var aggregates []StorageVolumeResourceAggregates
for _, v := range response.Aggregates {
var aggregate StorageVolumeResourceAggregates
aggregate.Name = types.StringValue(v.Name)
aggregates = append(aggregates, aggregate)
}
data.Aggregates = aggregates

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Expand Down Expand Up @@ -1057,7 +1082,19 @@

// ImportState imports a resource using ID from terraform import command by calling the Read method.
func (r *StorageVolumeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
idParts := strings.Split(req.ID, ",")

if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
resp.Diagnostics.AddError(
"Unexpected Import Identifier",
fmt.Sprintf("Expected import identifier with format: attr_one,attr_two,attr_three. Got: %q", req.ID),
Copy link
Contributor

@chuyich chuyich Nov 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we update the error message on attr_one,attr_two... with real parameter names? That will help the user know what need to put.
Expected import identifier with format: name,svm_name,cx_proflie_name. Got: xxx

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we should do that.

)
return
}

resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), idParts[0])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("svm_name"), idParts[1])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cx_profile_name"), idParts[2])...)
}

func readVolume(ctx context.Context, client *restclient.RestClient, data *StorageVolumeResourceModel) diag.Diagnostics {
Expand Down
9 changes: 9 additions & 0 deletions internal/provider/storage_volume_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ func TestAccStorageVolumeResource(t *testing.T) {
resource.TestCheckNoResourceAttr("netapp-ontap_storage_volume_resource.example", "volname"),
),
},
// Test importing a resource
{
ResourceName: "netapp-ontap_storage_volume_resource.example",
ImportState: true,
ImportStateId: fmt.Sprintf("%s,%s,%s", "automation_root", "automation", "cluster5"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("netapp-ontap_storage_volume_resource.example", "name", "automation"),
),
},
},
})
}
Expand Down
Loading