Skip to content

Commit

Permalink
SVM Import feature
Browse files Browse the repository at this point in the history
  • Loading branch information
suhasbshekar committed Nov 20, 2023
1 parent ee4b87c commit c53c042
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 23 deletions.
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_svm_resource**: Add support for import ([#6](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/6))

## 1.0.1 ()

BUG FIXES:
Expand Down
55 changes: 54 additions & 1 deletion docs/resources/svm_resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,57 @@ resource "netapp-ontap_svm_resource" "example" {
- `id` (String) svm identifier

## Import
Import is currently not support for this Resource.
This resource supports import, which allows you to import existing svms into the state of this resource.
Import require a unique ID composed of the svm name, and connection profile, separated by a comma.

id = `name`,`cx_profile_name`

### Terraform Import

For example
```shell
terraform import netapp-ontap_storage_svm_resource.example svm1,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_svm_resource.svm_import
id = "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_svm_resource" "svm_import" {
aggregates = [
{
name = "aggr1"
},
{
name = "aggr2"
},
{
name = "aggr3"
},
]
comment = "testing import"
cx_profile_name = "cluster4"
ipspace = "Default"
language = "c.utf_8"
max_volumes = "unlimited"
name = "testImport"
snapshot_policy = "default"
subtype = "default"
}
```
8 changes: 4 additions & 4 deletions internal/interfaces/svm.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ type SvmDataSourceFilterModel struct {
}

// GetSvm to get svm info by uuid
func GetSvm(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string) (*SvmGetDataModelONTAP, error) {
func GetSvm(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string) (*SvmGetDataSourceModel, error) {
statusCode, response, err := r.GetNilOrOneRecord("svm/svms/"+uuid, nil, nil)
if err != nil {
return nil, errorHandler.MakeAndReportError("error reading svm info", fmt.Sprintf("error on GET svm/svms: %s, statusCode %d", err, statusCode))
}

var dataONTAP *SvmGetDataModelONTAP
var dataONTAP *SvmGetDataSourceModel
if err := mapstructure.Decode(response, &dataONTAP); err != nil {
return nil, errorHandler.MakeAndReportError("failed to decode response from GET svm", fmt.Sprintf("error: %s, statusCode %d, response %#v", err, statusCode, response))
}
Expand All @@ -78,15 +78,15 @@ func GetSvm(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid stri
}

// GetSvmByName to get svm info by name
func GetSvmByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string) (*SvmGetDataModelONTAP, error) {
func GetSvmByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string) (*SvmGetDataSourceModel, error) {
query := r.NewQuery()
query.Add("name", name)
statusCode, response, err := r.GetNilOrOneRecord("svm/svms", query, nil)
if err != nil {
return nil, errorHandler.MakeAndReportError("error reading svm info", fmt.Sprintf("error on GET svm/svms: %s, statusCode %d", err, statusCode))
}

var dataONTAP *SvmGetDataModelONTAP
var dataONTAP *SvmGetDataSourceModel
if err := mapstructure.Decode(response, &dataONTAP); err != nil {
return nil, errorHandler.MakeAndReportError("failed to decode response from GET svm by name", fmt.Sprintf("error: %s, statusCode %d, response %#v", err, statusCode, response))
}
Expand Down
106 changes: 88 additions & 18 deletions internal/provider/svm_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package provider
import (
"context"
"fmt"
"strings"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
Expand Down Expand Up @@ -35,16 +36,21 @@ type SvmResource struct {

// SvmResourceModel describes the resource data model.
type SvmResourceModel struct {
CxProfileName types.String `tfsdk:"cx_profile_name"`
Name types.String `tfsdk:"name"`
Ipspace types.String `tfsdk:"ipspace"`
SnapshotPolicy types.String `tfsdk:"snapshot_policy"`
SubType types.String `tfsdk:"subtype"`
Comment types.String `tfsdk:"comment"`
Language types.String `tfsdk:"language"`
Aggregates []types.String `tfsdk:"aggregates"`
MaxVolumes types.String `tfsdk:"max_volumes"`
ID types.String `tfsdk:"id"`
CxProfileName types.String `tfsdk:"cx_profile_name"`
Name types.String `tfsdk:"name"`
Ipspace types.String `tfsdk:"ipspace"`
SnapshotPolicy types.String `tfsdk:"snapshot_policy"`
SubType types.String `tfsdk:"subtype"`
Comment types.String `tfsdk:"comment"`
Language types.String `tfsdk:"language"`
Aggregates []Aggregate `tfsdk:"aggregates"`
MaxVolumes types.String `tfsdk:"max_volumes"`
ID types.String `tfsdk:"id"`
}

// Aggregate describes the resource data model.
type Aggregate struct {
Name string `tfsdk:"name"`
}

// Metadata returns the resource type name.
Expand Down Expand Up @@ -87,10 +93,22 @@ func (r *SvmResource) Schema(ctx context.Context, req resource.SchemaRequest, re
MarkdownDescription: "Language to use for svm",
Optional: true,
},
"aggregates": schema.SetAttribute{
ElementType: types.StringType,
MarkdownDescription: "Aggregates to be assigned use for svm",
Optional: true,
// "aggregates": schema.SetAttribute{
// ElementType: types.StringType,
// MarkdownDescription: "Aggregates to be assigned use for svm",
// Optional: true,
// },
"aggregates": schema.SetNestedAttribute{
Required: true,
MarkdownDescription: "List of aggregates to place SVM on",
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"name": schema.StringAttribute{
MarkdownDescription: "Name of the aggregate",
Required: true,
},
},
},
},
"max_volumes": schema.StringAttribute{
MarkdownDescription: "Maximum number of volumes that can be created on the svm. Expects an integer or unlimited",
Expand Down Expand Up @@ -172,7 +190,7 @@ func (r *SvmResource) Create(ctx context.Context, req resource.CreateRequest, re
aggregates := []interfaces.Aggregate{}
for _, v := range data.Aggregates {
aggr := interfaces.Aggregate{}
aggr.Name = v.ValueString()
aggr.Name = v.Name
aggregates = append(aggregates, aggr)
}
err := mapstructure.Decode(aggregates, &request.Aggregates)
Expand Down Expand Up @@ -221,7 +239,13 @@ func (r *SvmResource) Read(ctx context.Context, req resource.ReadRequest, resp *
return
}
tflog.Debug(ctx, fmt.Sprintf("read a svm resource: %#v", data))
svm, err := interfaces.GetSvm(errorHandler, *client, data.ID.ValueString())
var svm *interfaces.SvmGetDataSourceModel
if data.ID.ValueString() != "" {
svm, err = interfaces.GetSvm(errorHandler, *client, data.ID.ValueString())
} else {
svm, err = interfaces.GetSvmByNameDataSource(errorHandler, *client, data.Name.ValueString())
}
// svm, err := interfaces.GetSvm(errorHandler, *client, data.ID.ValueString())
if err != nil {
return
}
Expand All @@ -230,6 +254,41 @@ func (r *SvmResource) Read(ctx context.Context, req resource.ReadRequest, resp *
}
data.Name = types.StringValue(svm.Name)
data.ID = types.StringValue(svm.UUID)
aggregates := []Aggregate{}

if len(svm.Aggregates) != 0 {
for _, v := range svm.Aggregates {
tflog.Debug(ctx, fmt.Sprintf("HIIIIIIIIII: %#v", v.Name))
aggr := Aggregate{}
aggr.Name = v.Name
aggregates = append(aggregates, aggr)
}
data.Aggregates = aggregates
}

if svm.Comment != "" {
data.Comment = types.StringValue(svm.Comment)
}

if svm.Ipspace.Name != "" {
data.Ipspace = types.StringValue(svm.Ipspace.Name)
}

if svm.SnapshotPolicy.Name != "" {
data.SnapshotPolicy = types.StringValue(svm.SnapshotPolicy.Name)
}

if svm.SubType != "" {
data.SubType = types.StringValue(svm.SubType)
}

if svm.Language != "" {
data.Language = types.StringValue(svm.Language)
}

if svm.MaxVolumes != "" {
data.MaxVolumes = types.StringValue(svm.MaxVolumes)
}

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
Expand Down Expand Up @@ -316,7 +375,7 @@ func (r *SvmResource) Update(ctx context.Context, req resource.UpdateRequest, re
if len(data.Aggregates) != 0 {
for _, v := range data.Aggregates {
aggr := interfaces.Aggregate{}
aggr.Name = v.ValueString()
aggr.Name = v.Name
aggregates = append(aggregates, aggr)
}
} else {
Expand Down Expand Up @@ -373,5 +432,16 @@ func (r *SvmResource) Delete(ctx context.Context, req resource.DeleteRequest, re

// ImportState imports a resource using ID from terraform import command by calling the Read method.
func (r *SvmResource) 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) != 2 || idParts[0] == "" || idParts[1] == "" {
resp.Diagnostics.AddError(
"Unexpected Import Identifier",
fmt.Sprintf("Expected import identifier with format: attr_one,attr_two. Got: %q", req.ID),
)
return
}

resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), idParts[0])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cx_profile_name"), idParts[1])...)
}
10 changes: 10 additions & 0 deletions internal/provider/svm_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ func TestAccSvmResource(t *testing.T) {
Config: testAccSvmResourceConfig("svm5", "carchi8py was here", "default"),
ExpectError: regexp.MustCompile("13434908"),
},
// Import and read
{
ResourceName: "netapp-ontap_svm_resource.example",
ImportState: true,
ImportStateId: fmt.Sprintf("%s,%s", "tfsvm4", "cluster4"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("netapp-ontap_svm_resource.example", "comment", "carchi8py was here"),
resource.TestCheckResourceAttr("netapp-ontap_svm_resource.example", "name", "tfsvm4"),
),
},
},
})
}
Expand Down

0 comments on commit c53c042

Please sign in to comment.