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  }