internal/provider/application_vmware_vm_resource.go (220 lines of code) (raw):
package provider
import (
"context"
"fmt"
"github.com/antihax/optional"
backupdr "github.com/umeshkumhar/backupdr-client"
"github.com/hashicorp/terraform-plugin-framework/attr"
"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/types"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &applicationVmwareVMsResource{}
_ resource.ResourceWithConfigure = &applicationVmwareVMsResource{}
_ resource.ResourceWithImportState = &applicationVmwareVMsResource{}
)
// NewApplicationVmwareVMsResource to create vCenter Host
func NewApplicationVmwareVMsResource() resource.Resource {
return &applicationVmwareVMsResource{}
}
// applicationVmwareVMsResource is the resource implementation.
type applicationVmwareVMsResource struct {
client *backupdr.APIClient
authCtx context.Context
}
// tf go model
type applicationVmwareVMsResourceModel struct {
ApplianceID types.String `tfsdk:"appliance_id"`
ClusterName types.String `tfsdk:"cluster_name"`
VMs []types.String `tfsdk:"vms"`
VcenterID types.String `tfsdk:"vcenter_id"`
Status types.String `tfsdk:"status"`
Applications types.List `tfsdk:"applications"`
}
// Metadata returns the resource type name.
func (r *applicationVmwareVMsResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_application_vmware_vm"
}
// Schema defines the schema for the resource.
func (r *applicationVmwareVMsResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Manages an vCenter Host to add Virtual Machines.",
MarkdownDescription: "You can use this resource to onboard VMware VMs as an application into the Backup and DR Service. After you onboard the application, you can perform backup or restore operations.",
Attributes: map[string]schema.Attribute{
"appliance_id": schema.StringAttribute{
Required: true,
MarkdownDescription: "Provide the backup/recovery appliance ID.",
},
"vcenter_id": schema.StringAttribute{
Required: true,
MarkdownDescription: "Provide the ID for the vCenter host.",
},
"cluster_name": schema.StringAttribute{
Required: true,
MarkdownDescription: "Provide a cluster name of the vCenter.",
},
"vms": schema.ListAttribute{
Required: true,
ElementType: types.StringType,
MarkdownDescription: "Provide the list of VMs UUID.",
},
"status": schema.StringAttribute{
Computed: true,
MarkdownDescription: "It displays the status of the request.",
},
"applications": schema.ListAttribute{
Computed: true,
ElementType: types.StringType,
MarkdownDescription: "It displays the list of application IDs.",
},
},
}
}
// Configure adds the provider configured client to the resource.
func (r *applicationVmwareVMsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
r.client = req.ProviderData.(*backupdrProvider).client
r.authCtx = req.ProviderData.(*backupdrProvider).authCtx
}
// Create a new resource.
func (r *applicationVmwareVMsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
// Retrieve values from plan
var plan applicationVmwareVMsResourceModel
var listVMs []string
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
for _, vm := range plan.VMs {
listVMs = append(listVMs, vm.ValueString())
}
reqVcenterHostAddVMs := backupdr.VmDiscoveryRest{
Cluster: plan.ApplianceID.ValueString(),
Addvms: true,
Vms: listVMs,
}
// Generate API request body from plan
reqBody := backupdr.HostApiVmAddNewOpts{
Body: optional.NewInterface(reqVcenterHostAddVMs),
}
// Add new VMs of vCenter Host
respObject, err := r.client.HostApi.VmAddNew(r.authCtx, plan.VcenterID.ValueString(), plan.ClusterName.ValueString(), &reqBody)
if err != nil {
resp.Diagnostics.AddError(
"Error adding VMs of the vCenter Host",
"Could not add VMs of vCenter Host, unexpected error: "+err.Error(),
)
return
}
if respObject.StatusCode == 200 || respObject.StatusCode == 204 {
// Map response body to schema and populate Computed attribute values
plan.Status = types.StringValue(respObject.Status)
applicationIDs := make([]attr.Value, 0)
for _, application := range listVMs {
filter := backupdr.ApplicationApiListApplicationsOpts{
Filter: optional.NewString(fmt.Sprintf("uniquename:==%s", application)),
}
// fetch application ID with filter
lsApps, res, err := r.client.ApplicationApi.ListApplications(r.authCtx, &filter)
if err != nil {
resp.Diagnostics.AddError(
"Error listing applications",
"Could not list applications, unexpected error: "+res.Status+" : "+err.Error(),
)
return
}
// capture the id of filtered application
if lsApps.Items != nil && len(lsApps.Items) > 0 {
applicationIDs = append(applicationIDs, types.StringValue(lsApps.Items[0].Id))
}
}
if len(applicationIDs) > 0 {
plan.Applications, diags = types.ListValue(types.StringType, applicationIDs)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
} else {
plan.Status = types.StringValue(respObject.Status)
resp.Diagnostics.AddError(
"Error adding VMs of the vCenter Host",
"Could not add VMs of vCenter Host, unexpected error: "+err.Error(),
)
return
}
// Set state to fully populated data
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
// Read resource information.
func (r *applicationVmwareVMsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
// Not implemented: no method exists to read vCenter VMs
return
}
func (r *applicationVmwareVMsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
// Retrieve values from plan
var plan applicationVmwareVMsResourceModel
var listVMs []string
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
for _, vm := range plan.VMs {
listVMs = append(listVMs, vm.ValueString())
}
reqVcenterHostAddVMs := backupdr.VmDiscoveryRest{
Cluster: plan.ApplianceID.ValueString(),
Addvms: true,
Vms: listVMs,
}
// Generate API request body from plan
reqBody := backupdr.HostApiVmAddNewOpts{
Body: optional.NewInterface(reqVcenterHostAddVMs),
}
// Add new VMs of vCenter Host
respObject, err := r.client.HostApi.VmAddNew(r.authCtx, plan.VcenterID.ValueString(), plan.ClusterName.ValueString(), &reqBody)
if err != nil {
resp.Diagnostics.AddError(
"Error updating VMs of the vCenter Host",
"Could not update VMs of vCenter Host, unexpected error: "+err.Error(),
)
return
}
if respObject.StatusCode == 200 || respObject.StatusCode == 204 {
// Map response body to schema and populate Computed attribute values
plan.Status = types.StringValue(respObject.Status)
applicationIDs := make([]attr.Value, 0)
applicationIDs = append(applicationIDs, plan.Applications.Elements()...)
for _, application := range listVMs {
filter := backupdr.ApplicationApiListApplicationsOpts{
Filter: optional.NewString(fmt.Sprintf("uniquename:==%s", application)),
}
// fetch application ID with filter
lsApps, res, err := r.client.ApplicationApi.ListApplications(r.authCtx, &filter)
if err != nil {
resp.Diagnostics.AddError(
"Error listing applications",
"Could not list applications, unexpected error: "+res.Status+" : "+err.Error(),
)
return
}
// capture the id of filtered application
if lsApps.Items != nil && len(lsApps.Items) > 0 {
applicationIDs = append(applicationIDs, types.StringValue(lsApps.Items[0].Id))
}
}
if len(applicationIDs) > 0 {
plan.Applications, diags = types.ListValue(types.StringType, applicationIDs)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
} else {
plan.Status = types.StringValue(respObject.Status)
resp.Diagnostics.AddError(
"Error updating VMs of the vCenter Host",
"Could not update VMs of vCenter Host, unexpected error: "+err.Error(),
)
return
}
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (r *applicationVmwareVMsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
// Not implemented: no method exists to read vCenter VMs
return
}
func (r *applicationVmwareVMsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Retrieve import ID and save to id attribute
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}