diff --git a/ibm/flex/structures.go b/ibm/flex/structures.go index 5dd3d383fb..41c407e251 100644 --- a/ibm/flex/structures.go +++ b/ibm/flex/structures.go @@ -3039,11 +3039,11 @@ func ResourceVolumeValidate(diff *schema.ResourceDiff) error { } } - if profile != "custom" { + if profile != "custom" && profile != "sdp" { if iops != 0 && diff.NewValueKnown("iops") && diff.HasChange("iops") { - return fmt.Errorf("VolumeError : iops is applicable for only custom volume profiles") + return fmt.Errorf("VolumeError : iops is applicable for only custom/sdp volume profiles") } - } else { + } else if profile != "sdp" { if capacity == 0 { capacity = int64(100) } diff --git a/ibm/service/vpc/data_source_ibm_is_volume.go b/ibm/service/vpc/data_source_ibm_is_volume.go index 95af9adeab..70e09d61bd 100644 --- a/ibm/service/vpc/data_source_ibm_is_volume.go +++ b/ibm/service/vpc/data_source_ibm_is_volume.go @@ -37,6 +37,23 @@ func DataSourceIBMISVolume() *schema.Resource { Computed: true, Description: "Indicates whether a running virtual server instance has an attachment to this volume.", }, + // defined_performance changes + "adjustable_capacity_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable capacity for this volume.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "adjustable_iops_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable IOPS for this volume.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, isVolumeAttachmentState: { Type: schema.TypeString, Computed: true, @@ -467,6 +484,15 @@ func volumeGet(d *schema.ResourceData, meta interface{}, name string) error { if vol.HealthState != nil { d.Set(isVolumeHealthState, *vol.HealthState) } + + if err = d.Set("adjustable_capacity_states", vol.AdjustableCapacityStates); err != nil { + return flex.DiscriminatedTerraformErrorf(err, fmt.Sprintf("Error setting adjustable_capacity_states: %s", err), "(Data) ibm_is_volume", "read", "set-adjustable_capacity_states") + } + + if err = d.Set("adjustable_iops_states", vol.AdjustableIopsStates); err != nil { + return flex.DiscriminatedTerraformErrorf(err, fmt.Sprintf("Error setting adjustable_iops_states: %s", err), "(Data) ibm_is_volume", "read", "set-adjustable_iops_states") + } + return nil } diff --git a/ibm/service/vpc/data_source_ibm_is_volume_profile.go b/ibm/service/vpc/data_source_ibm_is_volume_profile.go index 26dab62937..627335b481 100644 --- a/ibm/service/vpc/data_source_ibm_is_volume_profile.go +++ b/ibm/service/vpc/data_source_ibm_is_volume_profile.go @@ -4,7 +4,13 @@ package vpc import ( + "context" + "fmt" + "log" + + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" "github.com/IBM/vpc-go-sdk/vpcv1" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -15,7 +21,7 @@ const ( func DataSourceIBMISVolumeProfile() *schema.Resource { return &schema.Resource{ - Read: dataSourceIBMISVolumeProfileRead, + ReadContext: dataSourceIBMISVolumeProfileRead, Schema: map[string]*schema.Schema{ @@ -24,17 +30,202 @@ func DataSourceIBMISVolumeProfile() *schema.Resource { Required: true, Description: "Volume profile name", }, - - isVolumeProfileFamily: { + "boot_capacity": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "value": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The value for this profile field.", + }, + "default": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The default value for this profile field.", + }, + "max": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The maximum value for this profile field.", + }, + "min": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The minimum value for this profile field.", + }, + "step": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The increment step value for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The permitted values for this profile field.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, + }, + "capacity": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "value": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The value for this profile field.", + }, + "default": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The default value for this profile field.", + }, + "max": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The maximum value for this profile field.", + }, + "min": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The minimum value for this profile field.", + }, + "step": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The increment step value for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The permitted values for this profile field.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, + }, + "family": &schema.Schema{ Type: schema.TypeString, Computed: true, - Description: "Volume profile family", + Description: "The product family this volume profile belongs to.The enumerated values for this property may[expand](https://cloud.ibm.com/apidocs/vpc#property-value-expansion) in the future.", + }, + "href": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The URL for this volume profile.", + }, + "iops": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "value": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The value for this profile field.", + }, + "default": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The default value for this profile field.", + }, + "max": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The maximum value for this profile field.", + }, + "min": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The minimum value for this profile field.", + }, + "step": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The increment step value for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The permitted values for this profile field.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, + }, + // defined_performance changes + "adjustable_capacity_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable capacity for a volume with this profile.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "adjustable_iops_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable IOPS for a volume with this profile.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, }, }, } } -func dataSourceIBMISVolumeProfileRead(d *schema.ResourceData, meta interface{}) error { +func dataSourceIBMISVolumeProfileRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { name := d.Get(isVolumeProfile).(string) @@ -45,21 +236,334 @@ func dataSourceIBMISVolumeProfileRead(d *schema.ResourceData, meta interface{}) return nil } -func volumeProfileGet(d *schema.ResourceData, meta interface{}, name string) error { +func volumeProfileGet(d *schema.ResourceData, meta interface{}, name string) diag.Diagnostics { sess, err := vpcClient(meta) if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "(Data) ibm_is_volume_profile", "read") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } getVolumeProfileOptions := &vpcv1.GetVolumeProfileOptions{ Name: &name, } - profile, _, err := sess.GetVolumeProfile(getVolumeProfileOptions) + volumeProfile, _, err := sess.GetVolumeProfile(getVolumeProfileOptions) if err != nil { - return err + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("GetVolumeProfileWithContext failed: %s", err.Error()), "(Data) ibm_is_volume_profile", "read") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } // For lack of anything better, compose our id from profile name. - d.SetId(*profile.Name) - d.Set(isVolumeProfile, *profile.Name) - d.Set(isVolumeProfileFamily, *profile.Family) + d.SetId(*volumeProfile.Name) + + bootCapacity := []map[string]interface{}{} + if volumeProfile.BootCapacity != nil { + modelMap, err := DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityToMap(volumeProfile.BootCapacity) + if err != nil { + tfErr := flex.TerraformErrorf(err, err.Error(), "(Data) ibm_is_volume_profile", "read") + return tfErr.GetDiag() + } + bootCapacity = append(bootCapacity, modelMap) + } + if err = d.Set("boot_capacity", bootCapacity); err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("Error setting boot_capacity: %s", err), "(Data) ibm_is_volume_profile", "read") + return tfErr.GetDiag() + } + + capacity := []map[string]interface{}{} + if volumeProfile.Capacity != nil { + modelMap, err := DataSourceIBMIsVolumeProfileVolumeProfileCapacityToMap(volumeProfile.Capacity) + if err != nil { + tfErr := flex.TerraformErrorf(err, err.Error(), "(Data) ibm_is_volume_profile", "read") + return tfErr.GetDiag() + } + capacity = append(capacity, modelMap) + } + if err = d.Set("capacity", capacity); err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("Error setting capacity: %s", err), "(Data) ibm_is_volume_profile", "read") + return tfErr.GetDiag() + } + + if err = d.Set("family", volumeProfile.Family); err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("Error setting family: %s", err), "(Data) ibm_is_volume_profile", "read") + return tfErr.GetDiag() + } + + if err = d.Set("href", volumeProfile.Href); err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("Error setting href: %s", err), "(Data) ibm_is_volume_profile", "read") + return tfErr.GetDiag() + } + + iops := []map[string]interface{}{} + if volumeProfile.Iops != nil { + modelMap, err := DataSourceIBMIsVolumeProfileVolumeProfileIopsToMap(volumeProfile.Iops) + if err != nil { + tfErr := flex.TerraformErrorf(err, err.Error(), "(Data) ibm_is_volume_profile", "read") + return tfErr.GetDiag() + } + iops = append(iops, modelMap) + } + if err = d.Set("iops", iops); err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("Error setting iops: %s", err), "(Data) ibm_is_volume_profile", "read") + return tfErr.GetDiag() + } + // defined_performance changes + + adjustableCapacityStates := []map[string]interface{}{} + if volumeProfile.AdjustableCapacityStates != nil { + modelMap, err := DataSourceIBMIsVolumeProfileVolumeProfileAdjustableCapacityStatesToMap(volumeProfile.AdjustableCapacityStates) + if err != nil { + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "(Data) ibm_is_volume_profile", "read", "adjustable_capacity_states-to-map").GetDiag() + } + adjustableCapacityStates = append(adjustableCapacityStates, modelMap) + } + if err = d.Set("adjustable_capacity_states", adjustableCapacityStates); err != nil { + return flex.DiscriminatedTerraformErrorf(err, fmt.Sprintf("Error setting adjustable_capacity_states: %s", err), "(Data) ibm_is_volume_profile", "read", "set-adjustable_capacity_states").GetDiag() + } + + adjustableIopsStates := []map[string]interface{}{} + if volumeProfile.AdjustableIopsStates != nil { + modelMap, err := DataSourceIBMIsVolumeProfileVolumeProfileAdjustableIopsStatesToMap(volumeProfile.AdjustableIopsStates) + if err != nil { + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "(Data) ibm_is_volume_profile", "read", "adjustable_iops_states-to-map").GetDiag() + } + adjustableIopsStates = append(adjustableIopsStates, modelMap) + } + if err = d.Set("adjustable_iops_states", adjustableIopsStates); err != nil { + return flex.DiscriminatedTerraformErrorf(err, fmt.Sprintf("Error setting adjustable_iops_states: %s", err), "(Data) ibm_is_volume_profile", "read", "set-adjustable_iops_states").GetDiag() + } + return nil } + +func DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityToMap(model vpcv1.VolumeProfileBootCapacityIntf) (map[string]interface{}, error) { + if _, ok := model.(*vpcv1.VolumeProfileBootCapacityFixed); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityFixedToMap(model.(*vpcv1.VolumeProfileBootCapacityFixed)) + } else if _, ok := model.(*vpcv1.VolumeProfileBootCapacityRange); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityRangeToMap(model.(*vpcv1.VolumeProfileBootCapacityRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileBootCapacityEnum); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityEnumToMap(model.(*vpcv1.VolumeProfileBootCapacityEnum)) + } else if _, ok := model.(*vpcv1.VolumeProfileBootCapacityDependentRange); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityDependentRangeToMap(model.(*vpcv1.VolumeProfileBootCapacityDependentRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileBootCapacity); ok { + modelMap := make(map[string]interface{}) + model := model.(*vpcv1.VolumeProfileBootCapacity) + if model.Type != nil { + modelMap["type"] = *model.Type + } + if model.Value != nil { + modelMap["value"] = flex.IntValue(model.Value) + } + if model.Default != nil { + modelMap["default"] = flex.IntValue(model.Default) + } + if model.Max != nil { + modelMap["max"] = flex.IntValue(model.Max) + } + if model.Min != nil { + modelMap["min"] = flex.IntValue(model.Min) + } + if model.Step != nil { + modelMap["step"] = flex.IntValue(model.Step) + } + if model.Values != nil { + modelMap["values"] = model.Values + } + return modelMap, nil + } else { + return nil, fmt.Errorf("Unrecognized vpcv1.VolumeProfileBootCapacityIntf subtype encountered") + } +} + +func DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityFixedToMap(model *vpcv1.VolumeProfileBootCapacityFixed) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["type"] = *model.Type + modelMap["value"] = flex.IntValue(model.Value) + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityRangeToMap(model *vpcv1.VolumeProfileBootCapacityRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityEnumToMap(model *vpcv1.VolumeProfileBootCapacityEnum) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["type"] = *model.Type + modelMap["values"] = model.Values + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileBootCapacityDependentRangeToMap(model *vpcv1.VolumeProfileBootCapacityDependentRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileCapacityToMap(model vpcv1.VolumeProfileCapacityIntf) (map[string]interface{}, error) { + if _, ok := model.(*vpcv1.VolumeProfileCapacityFixed); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileCapacityFixedToMap(model.(*vpcv1.VolumeProfileCapacityFixed)) + } else if _, ok := model.(*vpcv1.VolumeProfileCapacityRange); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileCapacityRangeToMap(model.(*vpcv1.VolumeProfileCapacityRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileCapacityEnum); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileCapacityEnumToMap(model.(*vpcv1.VolumeProfileCapacityEnum)) + } else if _, ok := model.(*vpcv1.VolumeProfileCapacityDependentRange); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileCapacityDependentRangeToMap(model.(*vpcv1.VolumeProfileCapacityDependentRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileCapacity); ok { + modelMap := make(map[string]interface{}) + model := model.(*vpcv1.VolumeProfileCapacity) + if model.Type != nil { + modelMap["type"] = *model.Type + } + if model.Value != nil { + modelMap["value"] = flex.IntValue(model.Value) + } + if model.Default != nil { + modelMap["default"] = flex.IntValue(model.Default) + } + if model.Max != nil { + modelMap["max"] = flex.IntValue(model.Max) + } + if model.Min != nil { + modelMap["min"] = flex.IntValue(model.Min) + } + if model.Step != nil { + modelMap["step"] = flex.IntValue(model.Step) + } + if model.Values != nil { + modelMap["values"] = model.Values + } + return modelMap, nil + } else { + return nil, fmt.Errorf("Unrecognized vpcv1.VolumeProfileCapacityIntf subtype encountered") + } +} + +func DataSourceIBMIsVolumeProfileVolumeProfileCapacityFixedToMap(model *vpcv1.VolumeProfileCapacityFixed) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["type"] = *model.Type + modelMap["value"] = flex.IntValue(model.Value) + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileCapacityRangeToMap(model *vpcv1.VolumeProfileCapacityRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileCapacityEnumToMap(model *vpcv1.VolumeProfileCapacityEnum) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["type"] = *model.Type + modelMap["values"] = model.Values + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileCapacityDependentRangeToMap(model *vpcv1.VolumeProfileCapacityDependentRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileIopsToMap(model vpcv1.VolumeProfileIopsIntf) (map[string]interface{}, error) { + if _, ok := model.(*vpcv1.VolumeProfileIopsFixed); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileIopsFixedToMap(model.(*vpcv1.VolumeProfileIopsFixed)) + } else if _, ok := model.(*vpcv1.VolumeProfileIopsRange); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileIopsRangeToMap(model.(*vpcv1.VolumeProfileIopsRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileIopsEnum); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileIopsEnumToMap(model.(*vpcv1.VolumeProfileIopsEnum)) + } else if _, ok := model.(*vpcv1.VolumeProfileIopsDependentRange); ok { + return DataSourceIBMIsVolumeProfileVolumeProfileIopsDependentRangeToMap(model.(*vpcv1.VolumeProfileIopsDependentRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileIops); ok { + modelMap := make(map[string]interface{}) + model := model.(*vpcv1.VolumeProfileIops) + if model.Type != nil { + modelMap["type"] = *model.Type + } + if model.Value != nil { + modelMap["value"] = flex.IntValue(model.Value) + } + if model.Default != nil { + modelMap["default"] = flex.IntValue(model.Default) + } + if model.Max != nil { + modelMap["max"] = flex.IntValue(model.Max) + } + if model.Min != nil { + modelMap["min"] = flex.IntValue(model.Min) + } + if model.Step != nil { + modelMap["step"] = flex.IntValue(model.Step) + } + if model.Values != nil { + modelMap["values"] = model.Values + } + return modelMap, nil + } else { + return nil, fmt.Errorf("Unrecognized vpcv1.VolumeProfileIopsIntf subtype encountered") + } +} + +func DataSourceIBMIsVolumeProfileVolumeProfileIopsFixedToMap(model *vpcv1.VolumeProfileIopsFixed) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["type"] = *model.Type + modelMap["value"] = flex.IntValue(model.Value) + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileIopsRangeToMap(model *vpcv1.VolumeProfileIopsRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileIopsEnumToMap(model *vpcv1.VolumeProfileIopsEnum) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["type"] = *model.Type + modelMap["values"] = model.Values + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileIopsDependentRangeToMap(model *vpcv1.VolumeProfileIopsDependentRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileAdjustableCapacityStatesToMap(model *vpcv1.VolumeProfileAdjustableCapacityStates) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["type"] = *model.Type + modelMap["values"] = model.Values + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfileVolumeProfileAdjustableIopsStatesToMap(model *vpcv1.VolumeProfileAdjustableIopsStates) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["type"] = *model.Type + modelMap["values"] = model.Values + return modelMap, nil +} diff --git a/ibm/service/vpc/data_source_ibm_is_volume_profile_test.go b/ibm/service/vpc/data_source_ibm_is_volume_profile_test.go index f59a294373..baf5de3b26 100644 --- a/ibm/service/vpc/data_source_ibm_is_volume_profile_test.go +++ b/ibm/service/vpc/data_source_ibm_is_volume_profile_test.go @@ -29,11 +29,40 @@ func TestAccIBMISVolumeProfileDataSource_basic(t *testing.T) { }, }) } +func TestAccIBMISVolumeProfileDataSource_sdpbasic(t *testing.T) { + resName := "data.ibm_is_volume_profile.test1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISVolumeProfileDataSourceSdpConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resName, "name", "sdp"), + resource.TestCheckResourceAttrSet(resName, "family"), + resource.TestCheckResourceAttrSet(resName, "adjustable_capacity_states.#"), + resource.TestCheckResourceAttrSet(resName, "adjustable_capacity_states.0.values.#"), + resource.TestCheckResourceAttrSet(resName, "adjustable_iops_states.#"), + resource.TestCheckResourceAttrSet(resName, "adjustable_iops_states.0.values.#"), + resource.TestCheckResourceAttrSet(resName, "boot_capacity.#"), + resource.TestCheckResourceAttrSet(resName, "capacity.#"), + resource.TestCheckResourceAttrSet(resName, "iops.#"), + ), + }, + }, + }) +} func testAccCheckIBMISVolumeProfileDataSourceConfig() string { return fmt.Sprintf(` - -data "ibm_is_volume_profile" "test1" { - name = "%s" -}`, acc.VolumeProfileName) + data "ibm_is_volume_profile" "test1" { + name = "%s" + }`, acc.VolumeProfileName) +} +func testAccCheckIBMISVolumeProfileDataSourceSdpConfig() string { + return fmt.Sprintf(` + data "ibm_is_volume_profile" "test1" { + name = "%s" + }`, "sdp") } diff --git a/ibm/service/vpc/data_source_ibm_is_volume_profiles.go b/ibm/service/vpc/data_source_ibm_is_volume_profiles.go index 7315e82f3a..8599bf7ce4 100644 --- a/ibm/service/vpc/data_source_ibm_is_volume_profiles.go +++ b/ibm/service/vpc/data_source_ibm_is_volume_profiles.go @@ -4,11 +4,15 @@ package vpc import ( + "context" + "encoding/json" "fmt" + "log" "time" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" "github.com/IBM/vpc-go-sdk/vpcv1" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -18,7 +22,7 @@ const ( func DataSourceIBMISVolumeProfiles() *schema.Resource { return &schema.Resource{ - Read: dataSourceIBMISVolumeProfilesRead, + ReadContext: dataSourceIBMISVolumeProfilesRead, Schema: map[string]*schema.Schema{ @@ -28,13 +32,201 @@ func DataSourceIBMISVolumeProfiles() *schema.Resource { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, + "boot_capacity": &schema.Schema{ + Type: schema.TypeList, Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "value": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The value for this profile field.", + }, + "default": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The default value for this profile field.", + }, + "max": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The maximum value for this profile field.", + }, + "min": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The minimum value for this profile field.", + }, + "step": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The increment step value for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The permitted values for this profile field.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, }, - "family": { - Type: schema.TypeString, + "capacity": &schema.Schema{ + Type: schema.TypeList, Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "value": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The value for this profile field.", + }, + "default": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The default value for this profile field.", + }, + "max": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The maximum value for this profile field.", + }, + "min": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The minimum value for this profile field.", + }, + "step": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The increment step value for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The permitted values for this profile field.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, + }, + "family": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The product family this volume profile belongs to.The enumerated values for this property may[expand](https://cloud.ibm.com/apidocs/vpc#property-value-expansion) in the future.", + }, + "href": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The URL for this volume profile.", + }, + "iops": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "value": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The value for this profile field.", + }, + "default": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The default value for this profile field.", + }, + "max": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The maximum value for this profile field.", + }, + "min": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The minimum value for this profile field.", + }, + "step": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Description: "The increment step value for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The permitted values for this profile field.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The globally unique name for this volume profile.", + }, + // defined_performance changes + "adjustable_capacity_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable capacity for a volume with this profile.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "adjustable_iops_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The type for this profile field.", + }, + "values": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable IOPS for a volume with this profile.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, }, }, }, @@ -43,11 +235,13 @@ func DataSourceIBMISVolumeProfiles() *schema.Resource { } } -func dataSourceIBMISVolumeProfilesRead(d *schema.ResourceData, meta interface{}) error { +func dataSourceIBMISVolumeProfilesRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { err := volumeProfilesList(d, meta) if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "(Data) ibm_is_volume_profiles", "read") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } return nil } @@ -83,12 +277,12 @@ func volumeProfilesList(d *schema.ResourceData, meta interface{}) error { // } profilesInfo := make([]map[string]interface{}, 0) for _, profile := range allrecs { - - l := map[string]interface{}{ - "name": *profile.Name, - "family": *profile.Family, + modelMap, err := DataSourceIBMIsVolumeProfilesVolumeProfileToMap(&profile) + if err != nil { + tfErr := flex.TerraformErrorf(err, err.Error(), "(Data) ibm_is_volume_profiles", "read") + return tfErr } - profilesInfo = append(profilesInfo, l) + profilesInfo = append(profilesInfo, modelMap) } d.SetId(dataSourceIBMISVolumeProfilesID(d)) d.Set(isVolumeProfiles, profilesInfo) @@ -99,3 +293,273 @@ func volumeProfilesList(d *schema.ResourceData, meta interface{}) error { func dataSourceIBMISVolumeProfilesID(d *schema.ResourceData) string { return time.Now().UTC().String() } + +func DataSourceIBMIsVolumeProfilesVolumeProfileToMap(model *vpcv1.VolumeProfile) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + if model.BootCapacity != nil { + bootCapacityMap, err := DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityToMap(model.BootCapacity) + if err != nil { + return modelMap, err + } + modelMap["boot_capacity"] = []map[string]interface{}{bootCapacityMap} + } + if model.Capacity != nil { + capacityMap, err := DataSourceIBMIsVolumeProfilesVolumeProfileCapacityToMap(model.Capacity) + if err != nil { + return modelMap, err + } + modelMap["capacity"] = []map[string]interface{}{capacityMap} + } + modelMap["family"] = *model.Family + modelMap["href"] = *model.Href + if model.Iops != nil { + iopsMap, err := DataSourceIBMIsVolumeProfilesVolumeProfileIopsToMap(model.Iops) + if err != nil { + return modelMap, err + } + modelMap["iops"] = []map[string]interface{}{iopsMap} + } + modelMap["name"] = *model.Name + if model.AdjustableCapacityStates != nil { + adjustableCapacityStates, err := DataSourceIBMIsVolumeProfileVolumeProfileAdjustableCapacityStatesToMap(model.AdjustableCapacityStates) + if err != nil { + return modelMap, err + } + modelMap["adjustable_capacity_states"] = []map[string]interface{}{adjustableCapacityStates} + } + if model.AdjustableIopsStates != nil { + adjustableIopsStates, err := DataSourceIBMIsVolumeProfileVolumeProfileAdjustableIopsStatesToMap(model.AdjustableIopsStates) + if err != nil { + return modelMap, err + } + modelMap["adjustable_iops_states"] = []map[string]interface{}{adjustableIopsStates} + } + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityToMap(model vpcv1.VolumeProfileBootCapacityIntf) (map[string]interface{}, error) { + if _, ok := model.(*vpcv1.VolumeProfileBootCapacityFixed); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityFixedToMap(model.(*vpcv1.VolumeProfileBootCapacityFixed)) + } else if _, ok := model.(*vpcv1.VolumeProfileBootCapacityRange); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityRangeToMap(model.(*vpcv1.VolumeProfileBootCapacityRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileBootCapacityEnum); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityEnumToMap(model.(*vpcv1.VolumeProfileBootCapacityEnum)) + } else if _, ok := model.(*vpcv1.VolumeProfileBootCapacityDependentRange); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityDependentRangeToMap(model.(*vpcv1.VolumeProfileBootCapacityDependentRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileBootCapacity); ok { + modelMap := make(map[string]interface{}) + model := model.(*vpcv1.VolumeProfileBootCapacity) + if model.Type != nil { + modelMap["type"] = *model.Type + } + if model.Value != nil { + modelMap["value"] = flex.IntValue(model.Value) + } + if model.Default != nil { + modelMap["default"] = flex.IntValue(model.Default) + } + if model.Max != nil { + modelMap["max"] = flex.IntValue(model.Max) + } + if model.Min != nil { + modelMap["min"] = flex.IntValue(model.Min) + } + if model.Step != nil { + modelMap["step"] = flex.IntValue(model.Step) + } + if model.Values != nil { + modelMap["values"] = model.Values + } + return modelMap, nil + } else { + return nil, fmt.Errorf("Unrecognized vpcv1.VolumeProfileBootCapacityIntf subtype encountered") + } +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityFixedToMap(model *vpcv1.VolumeProfileBootCapacityFixed) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["type"] = *model.Type + modelMap["value"] = flex.IntValue(model.Value) + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityRangeToMap(model *vpcv1.VolumeProfileBootCapacityRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityEnumToMap(model *vpcv1.VolumeProfileBootCapacityEnum) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["type"] = *model.Type + modelMap["values"] = model.Values + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileBootCapacityDependentRangeToMap(model *vpcv1.VolumeProfileBootCapacityDependentRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileCapacityToMap(model vpcv1.VolumeProfileCapacityIntf) (map[string]interface{}, error) { + if _, ok := model.(*vpcv1.VolumeProfileCapacityFixed); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileCapacityFixedToMap(model.(*vpcv1.VolumeProfileCapacityFixed)) + } else if _, ok := model.(*vpcv1.VolumeProfileCapacityRange); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileCapacityRangeToMap(model.(*vpcv1.VolumeProfileCapacityRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileCapacityEnum); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileCapacityEnumToMap(model.(*vpcv1.VolumeProfileCapacityEnum)) + } else if _, ok := model.(*vpcv1.VolumeProfileCapacityDependentRange); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileCapacityDependentRangeToMap(model.(*vpcv1.VolumeProfileCapacityDependentRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileCapacity); ok { + modelMap := make(map[string]interface{}) + model := model.(*vpcv1.VolumeProfileCapacity) + if model.Type != nil { + modelMap["type"] = *model.Type + } + if model.Value != nil { + modelMap["value"] = flex.IntValue(model.Value) + } + if model.Default != nil { + modelMap["default"] = flex.IntValue(model.Default) + } + if model.Max != nil { + modelMap["max"] = flex.IntValue(model.Max) + } + if model.Min != nil { + modelMap["min"] = flex.IntValue(model.Min) + } + if model.Step != nil { + modelMap["step"] = flex.IntValue(model.Step) + } + if model.Values != nil { + modelMap["values"] = model.Values + } + return modelMap, nil + } else { + return nil, fmt.Errorf("Unrecognized vpcv1.VolumeProfileCapacityIntf subtype encountered") + } +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileCapacityFixedToMap(model *vpcv1.VolumeProfileCapacityFixed) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["type"] = *model.Type + modelMap["value"] = flex.IntValue(model.Value) + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileCapacityRangeToMap(model *vpcv1.VolumeProfileCapacityRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileCapacityEnumToMap(model *vpcv1.VolumeProfileCapacityEnum) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["type"] = *model.Type + modelMap["values"] = model.Values + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileCapacityDependentRangeToMap(model *vpcv1.VolumeProfileCapacityDependentRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileIopsToMap(model vpcv1.VolumeProfileIopsIntf) (map[string]interface{}, error) { + if _, ok := model.(*vpcv1.VolumeProfileIopsFixed); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileIopsFixedToMap(model.(*vpcv1.VolumeProfileIopsFixed)) + } else if _, ok := model.(*vpcv1.VolumeProfileIopsRange); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileIopsRangeToMap(model.(*vpcv1.VolumeProfileIopsRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileIopsEnum); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileIopsEnumToMap(model.(*vpcv1.VolumeProfileIopsEnum)) + } else if _, ok := model.(*vpcv1.VolumeProfileIopsDependentRange); ok { + return DataSourceIBMIsVolumeProfilesVolumeProfileIopsDependentRangeToMap(model.(*vpcv1.VolumeProfileIopsDependentRange)) + } else if _, ok := model.(*vpcv1.VolumeProfileIops); ok { + modelMap := make(map[string]interface{}) + model := model.(*vpcv1.VolumeProfileIops) + if model.Type != nil { + modelMap["type"] = *model.Type + } + if model.Value != nil { + modelMap["value"] = flex.IntValue(model.Value) + } + if model.Default != nil { + modelMap["default"] = flex.IntValue(model.Default) + } + if model.Max != nil { + modelMap["max"] = flex.IntValue(model.Max) + } + if model.Min != nil { + modelMap["min"] = flex.IntValue(model.Min) + } + if model.Step != nil { + modelMap["step"] = flex.IntValue(model.Step) + } + if model.Values != nil { + modelMap["values"] = model.Values + } + return modelMap, nil + } else { + return nil, fmt.Errorf("Unrecognized vpcv1.VolumeProfileIopsIntf subtype encountered") + } +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileIopsFixedToMap(model *vpcv1.VolumeProfileIopsFixed) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["type"] = *model.Type + modelMap["value"] = flex.IntValue(model.Value) + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileIopsRangeToMap(model *vpcv1.VolumeProfileIopsRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileIopsEnumToMap(model *vpcv1.VolumeProfileIopsEnum) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["default"] = flex.IntValue(model.Default) + modelMap["type"] = *model.Type + modelMap["values"] = model.Values + return modelMap, nil +} + +func DataSourceIBMIsVolumeProfilesVolumeProfileIopsDependentRangeToMap(model *vpcv1.VolumeProfileIopsDependentRange) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + modelMap["max"] = flex.IntValue(model.Max) + modelMap["min"] = flex.IntValue(model.Min) + modelMap["step"] = flex.IntValue(model.Step) + modelMap["type"] = *model.Type + return modelMap, nil +} + +func printfull(response interface{}) string { + output, err := json.MarshalIndent(response, "", " ") + if err == nil { + return fmt.Sprintf("%+v\n", string(output)) + } + return fmt.Sprintf("Error : %#v", response) +} diff --git a/ibm/service/vpc/data_source_ibm_is_volume_profiles_test.go b/ibm/service/vpc/data_source_ibm_is_volume_profiles_test.go index 75568c2633..5cee03d018 100644 --- a/ibm/service/vpc/data_source_ibm_is_volume_profiles_test.go +++ b/ibm/service/vpc/data_source_ibm_is_volume_profiles_test.go @@ -29,6 +29,29 @@ func TestAccIBMISVolumeProfilesDataSource_basic(t *testing.T) { }, }) } +func TestAccIBMISVolumeProfilesDataSource_Sdp(t *testing.T) { + resName := "data.ibm_is_volume_profiles.test1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISVolumeProfilesDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resName, "profiles.0.name"), + resource.TestCheckResourceAttrSet(resName, "profiles.0.family"), + resource.TestCheckResourceAttrSet(resName, "profiles.0.adjustable_capacity_states.#"), + resource.TestCheckResourceAttrSet(resName, "profiles.0.adjustable_iops_states.#"), + resource.TestCheckResourceAttrSet(resName, "profiles.0.boot_capacity.#"), + resource.TestCheckResourceAttrSet(resName, "profiles.0.capacity.#"), + resource.TestCheckResourceAttrSet(resName, "profiles.0.href"), + resource.TestCheckResourceAttrSet(resName, "profiles.0.iops.#"), + ), + }, + }, + }) +} func testAccCheckIBMISVolumeProfilesDataSourceConfig() string { // status filter defaults to empty diff --git a/ibm/service/vpc/data_source_ibm_is_volume_test.go b/ibm/service/vpc/data_source_ibm_is_volume_test.go index b35015d9b0..4c2b577530 100644 --- a/ibm/service/vpc/data_source_ibm_is_volume_test.go +++ b/ibm/service/vpc/data_source_ibm_is_volume_test.go @@ -49,6 +49,47 @@ func TestAccIBMISVolumeDatasource_basic(t *testing.T) { }, }) } +func TestAccIBMISVolumeDatasource_Sdp(t *testing.T) { + name := fmt.Sprintf("tf-vol-%d", acctest.RandIntRange(10, 100)) + zone := "eu-gb-1" + resName := "data.ibm_is_volume.testacc_dsvol" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISVolumeDataSourceSdpConfig(name, zone), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + resName, "name", name), + resource.TestCheckResourceAttr( + resName, "zone", zone), + resource.TestCheckResourceAttrSet( + resName, "active"), + resource.TestCheckResourceAttrSet( + resName, "attachment_state"), + resource.TestCheckResourceAttrSet( + resName, "bandwidth"), + resource.TestCheckResourceAttrSet( + resName, "busy"), + resource.TestCheckResourceAttrSet( + resName, "created_at"), + resource.TestCheckResourceAttrSet( + resName, "resource_group"), + resource.TestCheckResourceAttrSet( + resName, "profile"), + resource.TestCheckResourceAttrSet( + resName, "adjustable_capacity_states.#"), + resource.TestCheckResourceAttrSet( + resName, "adjustable_iops_states.#"), + resource.TestCheckResourceAttr( + resName, "profile", "sdp"), + ), + }, + }, + }) +} func TestAccIBMISVolumeDatasource_from_snapshot(t *testing.T) { resName := "data.ibm_is_volume.testacc_dsvol" @@ -152,6 +193,17 @@ func testAccCheckIBMISVolumeDataSourceConfig(name, zone string) string { name = ibm_is_volume.testacc_volume.name }`, name, zone) } +func testAccCheckIBMISVolumeDataSourceSdpConfig(name, zone string) string { + return fmt.Sprintf(` + resource "ibm_is_volume" "testacc_volume"{ + name = "%s" + profile = "sdp" + zone = "%s" + } + data "ibm_is_volume" "testacc_dsvol" { + name = ibm_is_volume.testacc_volume.name + }`, name, zone) +} func testAccCheckIBMISVolumeDataSourceWithCatalogOffering(vpcname, subnetname, sshname, publicKey, name, planCrn, versionCrn string) string { return fmt.Sprintf(` diff --git a/ibm/service/vpc/data_source_ibm_is_volumes.go b/ibm/service/vpc/data_source_ibm_is_volumes.go index 8a8e449da4..014814b4ea 100644 --- a/ibm/service/vpc/data_source_ibm_is_volumes.go +++ b/ibm/service/vpc/data_source_ibm_is_volumes.go @@ -392,6 +392,25 @@ func DataSourceIBMIsVolumes() *schema.Resource { }, }, }, + + // defined_performance changes + + "adjustable_capacity_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable capacity for this volume.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "adjustable_iops_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable IOPS for this volume.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, isVolumesStatus: &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -806,6 +825,8 @@ func dataSourceVolumeCollectionVolumesToMap(volumesItem vpcv1.Volume, meta inter } volumesMap[isVolumeHealthReasons] = healthReasonsList } + volumesMap["adjustable_capacity_states"] = volumesItem.AdjustableCapacityStates + volumesMap["adjustable_iops_states"] = volumesItem.AdjustableIopsStates if volumesItem.CatalogOffering != nil { versionCrn := "" if volumesItem.CatalogOffering.Version != nil && volumesItem.CatalogOffering.Version.CRN != nil { diff --git a/ibm/service/vpc/data_source_ibm_is_volumes_test.go b/ibm/service/vpc/data_source_ibm_is_volumes_test.go index 5e9a147e39..82b1339725 100644 --- a/ibm/service/vpc/data_source_ibm_is_volumes_test.go +++ b/ibm/service/vpc/data_source_ibm_is_volumes_test.go @@ -42,6 +42,24 @@ func TestAccIBMIsVolumesDataSourceBasic(t *testing.T) { }, }) } +func TestAccIBMIsVolumesDataSourceSdpBasic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckIBMIsVolumesDataSourceSdpConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.ibm_is_volumes.is_volumes", "id"), + resource.TestCheckResourceAttrSet("data.ibm_is_volumes.is_volumes", "volumes.#"), + resource.TestCheckResourceAttrSet("data.ibm_is_volumes.is_volumes", "volumes.0.adjustable_iops_states.#"), + resource.TestCheckResourceAttrSet("data.ibm_is_volumes.is_volumes", "volumes.0.adjustable_capacity_states.#"), + resource.TestCheckResourceAttr("data.ibm_is_volumes.is_volumes", "volumes.0.profile.0.name", "sdp"), + ), + }, + }, + }) +} func TestAccIBMIsVolumesFromSnapshotDataSourceBasic(t *testing.T) { resName := "data.ibm_is_volumes.is_volumes" vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) @@ -146,6 +164,12 @@ func testAccCheckIBMIsVolumesDataSourceConfigBasic() string { } `) } +func testAccCheckIBMIsVolumesDataSourceSdpConfig() string { + return fmt.Sprintf(` + data "ibm_is_volumes" "is_volumes" { + } + `) +} func testAccCheckIBMIsVolumesDataSourceConfigFilterByZone() string { return fmt.Sprintf(` diff --git a/ibm/service/vpc/resource_ibm_is_instance.go b/ibm/service/vpc/resource_ibm_is_instance.go index fe23b877e1..cb2e000aa3 100644 --- a/ibm/service/vpc/resource_ibm_is_instance.go +++ b/ibm/service/vpc/resource_ibm_is_instance.go @@ -1202,17 +1202,19 @@ func ResourceIBMISInstance() *schema.Resource { Computed: true, }, isInstanceBootSize: { - Type: schema.TypeInt, - Optional: true, - Computed: true, - ValidateFunc: validate.InvokeValidator("ibm_is_instance", isInstanceBootSize), + Type: schema.TypeInt, + Optional: true, + Computed: true, + // ValidateFunc: validate.InvokeValidator("ibm_is_instance", isInstanceBootSize), }, isInstanceBootIOPS: { Type: schema.TypeInt, Computed: true, + Optional: true, }, isInstanceBootProfile: { Type: schema.TypeString, + Optional: true, Computed: true, }, isInstanceBootVolumeTags: { @@ -1765,7 +1767,7 @@ func ResourceIBMISInstanceValidator() *validate.ResourceValidator { return &ibmISInstanceValidator } -func instanceCreateByImage(d *schema.ResourceData, meta interface{}, profile, name, vpcID, zone, image string) error { +func instanceCreateByImage(d *schema.ResourceData, meta interface{}, profile, name, vpcID, zone, image, bootProfile string) error { sess, err := vpcClient(meta) if err != nil { return err @@ -1854,6 +1856,12 @@ func instanceCreateByImage(d *schema.ResourceData, meta interface{}, profile, na sizeInt64 := int64(size) volTemplate.Capacity = &sizeInt64 } + iopsOk, ok := bootvol[isInstanceBootIOPS] + iops := iopsOk.(int) + if iops != 0 && ok { + iopsInt64 := int64(iops) + volTemplate.Iops = &iopsInt64 + } enc, ok := bootvol[isInstanceBootEncryption] encstr := enc.(string) if ok && encstr != "" { @@ -1861,10 +1869,11 @@ func instanceCreateByImage(d *schema.ResourceData, meta interface{}, profile, na CRN: &encstr, } } - - volprof := "general-purpose" + if bootProfile == "" { + bootProfile = "general-purpose" + } volTemplate.Profile = &vpcv1.VolumeProfileIdentity{ - Name: &volprof, + Name: &bootProfile, } var userTags *schema.Set if v, ok := bootvol[isInstanceBootVolumeTags]; ok { @@ -2315,6 +2324,12 @@ func instanceCreateByCatalogOffering(d *schema.ResourceData, meta interface{}, p sizeInt64 := int64(size) volTemplate.Capacity = &sizeInt64 } + iopsOk, ok := bootvol[isInstanceBootIOPS] + iops := iopsOk.(int) + if iops != 0 && ok { + iopsInt64 := int64(iops) + volTemplate.Iops = &iopsInt64 + } enc, ok := bootvol[isInstanceBootEncryption] encstr := enc.(string) if ok && encstr != "" { @@ -2734,6 +2749,12 @@ func instanceCreateByTemplate(d *schema.ResourceData, meta interface{}, profile, sizeInt64 := int64(size) volTemplate.Capacity = &sizeInt64 } + iopsOk, ok := bootvol[isInstanceBootIOPS] + iops := iopsOk.(int) + if iops != 0 && ok { + iopsInt64 := int64(iops) + volTemplate.Iops = &iopsInt64 + } enc, ok := bootvol[isInstanceBootEncryption] encstr := enc.(string) if ok && encstr != "" { @@ -3152,6 +3173,12 @@ func instanceCreateBySnapshot(d *schema.ResourceData, meta interface{}, profile, sizeInt64 := int64(size) volTemplate.Capacity = &sizeInt64 } + iopsOk, ok := bootvol[isInstanceBootIOPS] + iops := iopsOk.(int) + if iops != 0 && ok { + iopsInt64 := int64(iops) + volTemplate.Iops = &iopsInt64 + } enc, ok := bootvol[isInstanceBootEncryption] encstr := enc.(string) if ok && encstr != "" { @@ -3923,6 +3950,7 @@ func resourceIBMisInstanceCreate(d *schema.ResourceData, meta interface{}) error zone := d.Get(isInstanceZone).(string) image := d.Get(isInstanceImage).(string) snapshot := d.Get("boot_volume.0.snapshot").(string) + bootProfile := d.Get("boot_volume.0.profile").(string) snapshotcrn := d.Get("boot_volume.0.snapshot_crn").(string) volume := d.Get("boot_volume.0.volume_id").(string) template := d.Get(isInstanceSourceTemplate).(string) @@ -3952,7 +3980,7 @@ func resourceIBMisInstanceCreate(d *schema.ResourceData, meta interface{}) error return err } } else { - err := instanceCreateByImage(d, meta, profile, name, vpcID, zone, image) + err := instanceCreateByImage(d, meta, profile, name, vpcID, zone, image, bootProfile) if err != nil { return err } @@ -5116,6 +5144,7 @@ func instanceUpdate(d *schema.ResourceData, meta interface{}) error { } bootVolSize := "boot_volume.0.size" + bootIopsSize := "boot_volume.0.iops" if d.HasChange(bootVolSize) && !d.IsNewResource() { old, new := d.GetChange(bootVolSize) @@ -5149,6 +5178,36 @@ func instanceUpdate(d *schema.ResourceData, meta interface{}) error { return err } } + if d.HasChange(bootIopsSize) && !d.IsNewResource() { + _, new := d.GetChange(bootIopsSize) + + bootVolIops := int64(new.(int)) + volId := d.Get("boot_volume.0.volume_id").(string) + updateVolumeOptions := &vpcv1.UpdateVolumeOptions{ + ID: &volId, + } + volPatchModel := &vpcv1.VolumePatch{ + Iops: &bootVolIops, + } + volPatchModelAsPatch, err := volPatchModel.AsPatch() + + if err != nil { + return (fmt.Errorf("[ERROR] Error encountered while apply as patch for boot iops of instance %s", err)) + } + + updateVolumeOptions.VolumePatch = volPatchModelAsPatch + + vol, res, err := instanceC.UpdateVolume(updateVolumeOptions) + + if vol == nil || err != nil { + return (fmt.Errorf("[ERROR] Error encountered while expanding boot iops of instance %s/n%s", err, res)) + } + + _, err = isWaitForVolumeAvailable(instanceC, volId, d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return err + } + } bootVolTags := "boot_volume.0.tags" if d.HasChange(bootVolTags) && !d.IsNewResource() { var userTags *schema.Set diff --git a/ibm/service/vpc/resource_ibm_is_instance_test.go b/ibm/service/vpc/resource_ibm_is_instance_test.go index 0a9af1624d..f11dcbc6d4 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_test.go +++ b/ibm/service/vpc/resource_ibm_is_instance_test.go @@ -75,6 +75,99 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE }, }) } +func TestAccIBMISInstance_sdpbasic(t *testing.T) { + var instance string + vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) + name := fmt.Sprintf("tf-instnace-%d", acctest.RandIntRange(10, 100)) + subnetname := fmt.Sprintf("tf-subnet-%d", acctest.RandIntRange(10, 100)) + publicKey := strings.TrimSpace(` +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR +`) + sshname := fmt.Sprintf("tf-ssh-%d", acctest.RandIntRange(10, 100)) + userData1 := "a" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISInstanceSdpConfig(vpcname, subnetname, sshname, publicKey, name, userData1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceExists("ibm_is_instance.testacc_instance", instance), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "name", name), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "user_data", userData1), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.size", "250"), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.iops", "10000"), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.profile", "sdp"), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "zone", acc.ISZoneName), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "vcpu.#"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "vcpu.0.manufacturer"), + ), + }, + { + Config: testAccCheckIBMISInstanceSdpCapacityUpdateConfig(vpcname, subnetname, sshname, publicKey, name, userData1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceExists("ibm_is_instance.testacc_instance", instance), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "name", name), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "user_data", userData1), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "zone", acc.ISZoneName), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.size", "25000"), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.iops", "10000"), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.profile", "sdp"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "primary_network_interface.0.port_speed"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "vcpu.#"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "vcpu.0.manufacturer"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "numa_count"), + ), + }, + { + Config: testAccCheckIBMISInstanceSdpIopsUpdateConfig(vpcname, subnetname, sshname, publicKey, name, userData1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceExists("ibm_is_instance.testacc_instance", instance), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "name", name), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "user_data", userData1), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "zone", acc.ISZoneName), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.size", "25000"), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.iops", "28000"), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "boot_volume.0.profile", "sdp"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "primary_network_interface.0.port_speed"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "vcpu.#"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "vcpu.0.manufacturer"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance.testacc_instance", "numa_count"), + ), + }, + }, + }) +} func TestAccIBMISInstanceWithoutKeys_basic(t *testing.T) { var instance string @@ -1457,6 +1550,114 @@ func testAccCheckIBMISInstanceConfig(vpcname, subnetname, sshname, publicKey, na } }`, vpcname, subnetname, acc.ISZoneName, acc.ISCIDR, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, userData, acc.ISZoneName) } +func testAccCheckIBMISInstanceSdpConfig(vpcname, subnetname, sshname, publicKey, name, userData string) string { + return fmt.Sprintf(` + resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" + } + + resource "ibm_is_subnet" "testacc_subnet" { + name = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + ipv4_cidr_block = "%s" + } + + resource "ibm_is_ssh_key" "testacc_sshkey" { + name = "%s" + public_key = "%s" + } + + resource "ibm_is_instance" "testacc_instance" { + name = "%s" + image = "%s" + profile = "%s" + boot_volume { + size = 250 + profile = "sdp" + iops = 10000 + } + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + user_data = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + }`, vpcname, subnetname, acc.ISZoneName, acc.ISCIDR, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, userData, acc.ISZoneName) +} +func testAccCheckIBMISInstanceSdpIopsUpdateConfig(vpcname, subnetname, sshname, publicKey, name, userData string) string { + return fmt.Sprintf(` + resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" + } + + resource "ibm_is_subnet" "testacc_subnet" { + name = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + ipv4_cidr_block = "%s" + } + + resource "ibm_is_ssh_key" "testacc_sshkey" { + name = "%s" + public_key = "%s" + } + + resource "ibm_is_instance" "testacc_instance" { + name = "%s" + image = "%s" + profile = "%s" + boot_volume { + size = 25000 + profile = "sdp" + iops = 28000 + } + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + user_data = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + }`, vpcname, subnetname, acc.ISZoneName, acc.ISCIDR, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, userData, acc.ISZoneName) +} +func testAccCheckIBMISInstanceSdpCapacityUpdateConfig(vpcname, subnetname, sshname, publicKey, name, userData string) string { + return fmt.Sprintf(` + resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" + } + + resource "ibm_is_subnet" "testacc_subnet" { + name = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + ipv4_cidr_block = "%s" + } + + resource "ibm_is_ssh_key" "testacc_sshkey" { + name = "%s" + public_key = "%s" + } + + resource "ibm_is_instance" "testacc_instance" { + name = "%s" + image = "%s" + profile = "%s" + boot_volume { + size = 25000 + profile = "sdp" + iops = 10000 + } + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + user_data = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + }`, vpcname, subnetname, acc.ISZoneName, acc.ISCIDR, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, userData, acc.ISZoneName) +} func testAccCheckIBMISInstanceWithoutKeysConfig(vpcname, subnetname, name, userData string) string { return fmt.Sprintf(` diff --git a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go index 1e1075549b..8557117de3 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go +++ b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go @@ -245,7 +245,7 @@ func ResourceIBMISInstanceVolumeAttachmentValidator() *validate.ResourceValidato ValidateFunctionIdentifier: validate.IntBetween, Type: validate.TypeInt, MinValue: "10", - MaxValue: "16000"}) + MaxValue: "64000"}) validateSchema = append(validateSchema, validate.ValidateSchema{ @@ -263,7 +263,7 @@ func ResourceIBMISInstanceVolumeAttachmentValidator() *validate.ResourceValidato ValidateFunctionIdentifier: validate.ValidateAllowedStringValue, Type: validate.TypeString, Optional: true, - AllowedValues: "general-purpose, 5iops-tier, 10iops-tier, custom", + AllowedValues: "general-purpose, 5iops-tier, 10iops-tier, custom, sdp", }) validateSchema = append(validateSchema, validate.ValidateSchema{ @@ -367,7 +367,10 @@ func instanceVolAttachmentCreate(d *schema.ResourceData, meta interface{}, insta if iops != 0 { volProtoVol.Iops = &iops } - volProfileStr := "custom" + volProfileStr := d.Get(isInstanceVolProfile).(string) + if volProfileStr == "" { + volProfileStr = "custom" + } volProtoVol.Profile = &vpcv1.VolumeProfileIdentity{ Name: &volProfileStr, } @@ -595,8 +598,39 @@ func instanceVolAttUpdate(d *schema.ResourceData, meta interface{}) error { if volIdOk, ok := d.GetOk(isInstanceVolAttVol); ok { volId = volIdOk.(string) } - - if volId != "" && (d.HasChange(isInstanceVolIops) || d.HasChange(isInstanceVolProfile)) { // || d.HasChange(isInstanceVolAttTags) + volProfile := "" + if volProfileOk, ok := d.GetOk(isInstanceVolProfile); ok { + volProfile = volProfileOk.(string) + } + if volId != "" && d.HasChange(isInstanceVolIops) && !d.HasChange(isInstanceVolProfile) && volProfile == "sdp" { // || d.HasChange(isInstanceVolAttTags) + updateVolumeProfileOptions := &vpcv1.UpdateVolumeOptions{ + ID: &volId, + } + volumeProfilePatchModel := &vpcv1.VolumePatch{} + if d.HasChange(isVolumeIops) { + iops := int64(d.Get(isVolumeIops).(int)) + volumeProfilePatchModel.Iops = &iops + } + volumeProfilePatch, err := volumeProfilePatchModel.AsPatch() + if err != nil { + return fmt.Errorf("[ERROR] Error calling asPatch for volumeProfilePatch: %s", err) + } + optionsget := &vpcv1.GetVolumeOptions{ + ID: &volId, + } + _, response, err := instanceC.GetVolume(optionsget) + if err != nil { + return fmt.Errorf("[ERROR] Error getting Boot Volume (%s): %s\n%s", id, err, response) + } + eTag := response.Headers.Get("ETag") + updateVolumeProfileOptions.IfMatch = &eTag + updateVolumeProfileOptions.VolumePatch = volumeProfilePatch + _, response, err = instanceC.UpdateVolume(updateVolumeProfileOptions) + if err != nil { + return fmt.Errorf("[ERROR] Error updating volume profile/iops/userTags: %s\n%s", err, response) + } + isWaitForVolumeAvailable(instanceC, volId, d.Timeout(schema.TimeoutCreate)) + } else if volId != "" && (d.HasChange(isInstanceVolIops) || d.HasChange(isInstanceVolProfile)) { // || d.HasChange(isInstanceVolAttTags) insId := d.Get(isInstanceId).(string) getinsOptions := &vpcv1.GetInstanceOptions{ ID: &insId, @@ -691,30 +725,31 @@ func instanceVolAttUpdate(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("[ERROR] Error Getting Volume (%s): %s\n%s", id, err, response) } + if *vol.Profile.Name != "sdp" { + if vol.VolumeAttachments == nil || len(vol.VolumeAttachments) == 0 || *vol.VolumeAttachments[0].Name == "" { + return fmt.Errorf("[ERROR] Error volume capacity can't be updated since volume %s is not attached to any instance for VolumePatch", id) + } - if vol.VolumeAttachments == nil || len(vol.VolumeAttachments) == 0 || *vol.VolumeAttachments[0].Name == "" { - return fmt.Errorf("[ERROR] Error volume capacity can't be updated since volume %s is not attached to any instance for VolumePatch", id) - } - - getinsOptions := &vpcv1.GetInstanceOptions{ - ID: &instanceId, - } - instance, response, err := instanceC.GetInstance(getinsOptions) - if err != nil || instance == nil { - return fmt.Errorf("[ERROR] Error retrieving Instance (%s) : %s\n%s", instanceId, err, response) - } - if instance != nil && *instance.Status != "running" { - actiontype := "start" - createinsactoptions := &vpcv1.CreateInstanceActionOptions{ - InstanceID: &instanceId, - Type: &actiontype, + getinsOptions := &vpcv1.GetInstanceOptions{ + ID: &instanceId, } - _, response, err = instanceC.CreateInstanceAction(createinsactoptions) - if err != nil { + instance, response, err := instanceC.GetInstance(getinsOptions) + if err != nil || instance == nil { + return fmt.Errorf("[ERROR] Error retrieving Instance (%s) : %s\n%s", instanceId, err, response) + } + if instance != nil && *instance.Status != "running" { + actiontype := "start" + createinsactoptions := &vpcv1.CreateInstanceActionOptions{ + InstanceID: &instanceId, + Type: &actiontype, + } + _, response, err = instanceC.CreateInstanceAction(createinsactoptions) + if err != nil { + return fmt.Errorf("[ERROR] Error starting Instance (%s) : %s\n%s", instanceId, err, response) + } + _, err = isWaitForInstanceAvailable(instanceC, instanceId, d.Timeout(schema.TimeoutCreate), d) return fmt.Errorf("[ERROR] Error starting Instance (%s) : %s\n%s", instanceId, err, response) } - _, err = isWaitForInstanceAvailable(instanceC, instanceId, d.Timeout(schema.TimeoutCreate), d) - return fmt.Errorf("[ERROR] Error starting Instance (%s) : %s\n%s", instanceId, err, response) } capacity := int64(d.Get(isVolumeCapacity).(int)) updateVolumeOptions := &vpcv1.UpdateVolumeOptions{ diff --git a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go index 6e99984c0f..0233ec7c17 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go +++ b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go @@ -85,6 +85,90 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE }, }) } +func TestAccIBMISInstanceVolumeAttachment_sdpbasic(t *testing.T) { + var instanceVolAtt string + vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) + name := fmt.Sprintf("tf-instnace-%d", acctest.RandIntRange(10, 100)) + subnetname := fmt.Sprintf("tf-subnet-%d", acctest.RandIntRange(10, 100)) + publicKey := strings.TrimSpace(` +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR +`) + sshname := fmt.Sprintf("tf-ssh-%d", acctest.RandIntRange(10, 100)) + attName := fmt.Sprintf("tf-volatt-%d", acctest.RandIntRange(10, 100)) + autoDelete := true + volName := fmt.Sprintf("tf-vol-%d", acctest.RandIntRange(10, 100)) + volUpdateName := fmt.Sprintf("tf-vol-update-%d", acctest.RandIntRange(10, 100)) + iops1 := int64(10000) + iops2 := int64(28000) + + capacity1 := int64(1000) + capacity2 := int64(22000) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISInstanceVolumeAttachmentSdpConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity1, iops1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "name", attName), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity1)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "iops", "10000"), + ), + }, + { + Config: testAccCheckIBMISInstanceVolumeAttachmentSdpConfig(vpcname, subnetname, sshname, publicKey, name, attName, volUpdateName, autoDelete, capacity1, iops1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "name", attName), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity1)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "iops", "10000"), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "volume_name", volUpdateName), + ), + }, + { + Config: testAccCheckIBMISInstanceVolumeAttachmentSdpConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity1, iops2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "name", attName), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity1)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "iops", "28000"), + ), + }, + + { + Config: testAccCheckIBMISInstanceVolumeAttachmentSdpConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity2, iops2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "name", attName), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity2)), + ), + }, + }, + }) +} func TestAccIBMISInstanceVolumeAttachment_crn(t *testing.T) { var instanceVolAtt string vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) @@ -277,6 +361,54 @@ func testAccCheckIBMISInstanceVolumeAttachmentConfig(vpcname, subnetname, sshnam `, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, attName, capacity, iops, autoDelete, volName) } +func testAccCheckIBMISInstanceVolumeAttachmentSdpConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName string, autoDelete bool, capacity, iops int64) string { + return fmt.Sprintf(` + resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" + } + + resource "ibm_is_subnet" "testacc_subnet" { + name = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + total_ipv4_address_count = 16 + } + + resource "ibm_is_ssh_key" "testacc_sshkey" { + name = "%s" + public_key = "%s" + } + + resource "ibm_is_instance" "testacc_instance" { + name = "%s" + image = "%s" + profile = "%s" + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + network_interfaces { + subnet = ibm_is_subnet.testacc_subnet.id + name = "eth1" + } + } + resource "ibm_is_instance_volume_attachment" "testacc_att" { + instance = ibm_is_instance.testacc_instance.id + + name = "%s" + profile = "sdp" + capacity = %d + iops = %d + + delete_volume_on_instance_delete = %t + volume_name = "%s" + } + + `, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, attName, capacity, iops, autoDelete, volName) +} + func testAccCheckIBMISInstanceVolumeAttachmentCrnConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName string, autoDelete bool) string { return fmt.Sprintf(` resource "ibm_is_vpc" "testacc_vpc" { diff --git a/ibm/service/vpc/resource_ibm_is_volume.go b/ibm/service/vpc/resource_ibm_is_volume.go index 0fcf79e00a..210437396b 100644 --- a/ibm/service/vpc/resource_ibm_is_volume.go +++ b/ibm/service/vpc/resource_ibm_is_volume.go @@ -124,12 +124,12 @@ func ResourceIBMISVolume() *schema.Resource { }, isVolumeCapacity: { - Type: schema.TypeInt, - Optional: true, - ForceNew: false, - Computed: true, - ValidateFunc: validate.InvokeValidator("ibm_is_volume", isVolumeCapacity), - Description: "Volume capacity value", + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + Computed: true, + // ValidateFunc: validate.InvokeValidator("ibm_is_volume", isVolumeCapacity), + Description: "Volume capacity value", }, isVolumeSourceSnapshot: { Type: schema.TypeString, @@ -156,11 +156,11 @@ func ResourceIBMISVolume() *schema.Resource { Description: "Resource group name", }, isVolumeIops: { - Type: schema.TypeInt, - Optional: true, - Computed: true, - ValidateFunc: validate.InvokeValidator("ibm_is_volume", isVolumeIops), - Description: "IOPS value for the Volume", + Type: schema.TypeInt, + Optional: true, + Computed: true, + // ValidateFunc: validate.InvokeValidator("ibm_is_volume", isVolumeIops), + Description: "IOPS value for the Volume", }, isVolumeCrn: { Type: schema.TypeString, @@ -231,6 +231,19 @@ func ResourceIBMISVolume() *schema.Resource { }, }, }, + // defined_performance changes + "adjustable_capacity_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable capacity for this volume.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "adjustable_iops_states": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The attachment states that support adjustable IOPS for this volume.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, isVolumeHealthReasons: { Type: schema.TypeList, Computed: true, @@ -410,22 +423,20 @@ func ResourceIBMISVolumeValidator() *validate.ResourceValidator { ValidateFunctionIdentifier: validate.ValidateAllowedStringValue, Type: validate.TypeString, Optional: true, - AllowedValues: "general-purpose, 5iops-tier, 10iops-tier, custom", + AllowedValues: "general-purpose, 5iops-tier, 10iops-tier, custom, sdp", }) validateSchema = append(validateSchema, validate.ValidateSchema{ Identifier: isVolumeCapacity, ValidateFunctionIdentifier: validate.IntBetween, Type: validate.TypeInt, - MinValue: "10", - MaxValue: "16000"}) + MinValue: "10"}) validateSchema = append(validateSchema, validate.ValidateSchema{ Identifier: isVolumeIops, ValidateFunctionIdentifier: validate.IntBetween, Type: validate.TypeInt, - MinValue: "100", - MaxValue: "48000"}) + MinValue: "100"}) validateSchema = append(validateSchema, validate.ValidateSchema{ Identifier: "accesstag", @@ -716,6 +727,16 @@ func volGet(d *schema.ResourceData, meta interface{}, id string) error { if err != nil { return err } + // defined_performance changes + + if err = d.Set("adjustable_capacity_states", vol.AdjustableCapacityStates); err != nil { + err = fmt.Errorf("Error setting adjustable_capacity_states: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_is_volume", "read", "set-adjustable_capacity_states") + } + if err = d.Set("adjustable_iops_states", vol.AdjustableIopsStates); err != nil { + err = fmt.Errorf("Error setting adjustable_iops_states: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_is_volume", "read", "set-adjustable_iops_states") + } d.Set(flex.ResourceControllerURL, controller+"/vpc-ext/storage/storageVolumes") d.Set(flex.ResourceName, *vol.Name) d.Set(flex.ResourceCRN, *vol.CRN) @@ -786,7 +807,7 @@ func volUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasNam optionsget := &vpcv1.GetVolumeOptions{ ID: &id, } - _, response, err := sess.GetVolume(optionsget) + oldVol, response, err := sess.GetVolume(optionsget) if err != nil { if response != nil && response.StatusCode == 404 { d.SetId("") @@ -809,15 +830,39 @@ func volUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasNam return fmt.Errorf("[ERROR] Error calling asPatch for volumeNamePatch: %s", err) } options.VolumePatch = volumeNamePatch - _, _, err = sess.UpdateVolume(options) + _, response, err = sess.UpdateVolume(options) + if err != nil { + return err + } _, err = isWaitForVolumeAvailable(sess, d.Id(), d.Timeout(schema.TimeoutCreate)) if err != nil { return err } + eTag = response.Headers.Get("ETag") + options.IfMatch = &eTag } // profile/ iops update - if d.HasChange(isVolumeProfileName) || d.HasChange(isVolumeIops) { + if !d.HasChange(isVolumeProfileName) && *oldVol.Profile.Name == "sdp" && d.HasChange(isVolumeIops) { + volumeProfilePatchModel := &vpcv1.VolumePatch{} + iops := int64(d.Get(isVolumeIops).(int)) + volumeProfilePatchModel.Iops = &iops + volumeProfilePatch, err := volumeProfilePatchModel.AsPatch() + if err != nil { + return fmt.Errorf("[ERROR] Error calling asPatch for VolumeProfilePatch for sdp profiles : %s", err) + } + options.VolumePatch = volumeProfilePatch + _, response, err = sess.UpdateVolume(options) + if err != nil { + return err + } + _, err = isWaitForVolumeAvailable(sess, d.Id(), d.Timeout(schema.TimeoutCreate)) + if err != nil { + return err + } + eTag = response.Headers.Get("ETag") + options.IfMatch = &eTag + } else if d.HasChange(isVolumeProfileName) || d.HasChange(isVolumeIops) { volumeProfilePatchModel := &vpcv1.VolumePatch{} volId := d.Id() getvoloptions := &vpcv1.GetVolumeOptions{ @@ -874,6 +919,11 @@ func volUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasNam } options.VolumePatch = volumeProfilePatch _, response, err = sess.UpdateVolume(options) + if err != nil { + return err + } + eTag = response.Headers.Get("ETag") + options.IfMatch = &eTag _, err = isWaitForVolumeAvailable(sess, d.Id(), d.Timeout(schema.TimeoutCreate)) if err != nil { return err @@ -894,32 +944,37 @@ func volUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasNam } return fmt.Errorf("[ERROR] Error Getting Volume (%s): %s\n%s", id, err, response) } - if vol.VolumeAttachments == nil || len(vol.VolumeAttachments) == 0 || *vol.VolumeAttachments[0].ID == "" { - return fmt.Errorf("[ERROR] Error volume capacity can't be updated since volume %s is not attached to any instance for VolumePatch", id) - } - insId := vol.VolumeAttachments[0].Instance.ID - getinsOptions := &vpcv1.GetInstanceOptions{ - ID: insId, - } - instance, response, err := sess.GetInstance(getinsOptions) - if err != nil || instance == nil { - return fmt.Errorf("[ERROR] Error retrieving Instance (%s) : %s\n%s", *insId, err, response) - } - if instance != nil && *instance.Status != "running" { - actiontype := "start" - createinsactoptions := &vpcv1.CreateInstanceActionOptions{ - InstanceID: insId, - Type: &actiontype, + eTag = response.Headers.Get("ETag") + options.IfMatch = &eTag + if *vol.Profile.Name != "sdp" { + if vol.VolumeAttachments == nil || len(vol.VolumeAttachments) == 0 || *vol.VolumeAttachments[0].ID == "" { + return fmt.Errorf("[ERROR] Error volume capacity can't be updated since volume %s is not attached to any instance for VolumePatch", id) } - _, response, err = sess.CreateInstanceAction(createinsactoptions) - if err != nil { - return fmt.Errorf("[ERROR] Error starting Instance (%s) : %s\n%s", *insId, err, response) + insId := vol.VolumeAttachments[0].Instance.ID + getinsOptions := &vpcv1.GetInstanceOptions{ + ID: insId, } - _, err = isWaitForInstanceAvailable(sess, *insId, d.Timeout(schema.TimeoutCreate), d) - if err != nil { - return err + instance, response, err := sess.GetInstance(getinsOptions) + if err != nil || instance == nil { + return fmt.Errorf("[ERROR] Error retrieving Instance (%s) : %s\n%s", *insId, err, response) + } + if instance != nil && *instance.Status != "running" { + actiontype := "start" + createinsactoptions := &vpcv1.CreateInstanceActionOptions{ + InstanceID: insId, + Type: &actiontype, + } + _, response, err = sess.CreateInstanceAction(createinsactoptions) + if err != nil { + return fmt.Errorf("[ERROR] Error starting Instance (%s) : %s\n%s", *insId, err, response) + } + _, err = isWaitForInstanceAvailable(sess, *insId, d.Timeout(schema.TimeoutCreate), d) + if err != nil { + return err + } } } + capacity = int64(d.Get(isVolumeCapacity).(int)) volumeCapacityPatchModel := &vpcv1.VolumePatch{} volumeCapacityPatchModel.Capacity = &capacity diff --git a/ibm/service/vpc/resource_ibm_is_volume_test.go b/ibm/service/vpc/resource_ibm_is_volume_test.go index da6f147ca1..df86461ad7 100644 --- a/ibm/service/vpc/resource_ibm_is_volume_test.go +++ b/ibm/service/vpc/resource_ibm_is_volume_test.go @@ -48,6 +48,106 @@ func TestAccIBMISVolume_basic(t *testing.T) { }, }) } +func TestAccIBMISVolume_sdp(t *testing.T) { + var vol string + name := fmt.Sprintf("tf-vol-%d", acctest.RandIntRange(10, 100)) + name1 := fmt.Sprintf("tf-vol-upd-%d", acctest.RandIntRange(10, 100)) + capacity1 := 16000 + capacity2 := 32000 + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISVolumeSdpConfig(name, capacity1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISVolumeExists("ibm_is_volume.storage", vol), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "name", name), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "capacity", fmt.Sprintf("%d", capacity1)), + ), + }, + + { + Config: testAccCheckIBMISVolumeSdpConfig(name1, capacity2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISVolumeExists("ibm_is_volume.storage", vol), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "name", name1), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "capacity", fmt.Sprintf("%d", capacity2)), + ), + }, + }, + }) +} +func TestAccIBMISVolume_sdpUpdate(t *testing.T) { + var vol string + name := fmt.Sprintf("tf-vol-%d", acctest.RandIntRange(10, 100)) + name1 := fmt.Sprintf("tf-vol-upd-%d", acctest.RandIntRange(10, 100)) + capacity1 := 16000 + capacity2 := 32000 + iops1 := 10000 + iops2 := 28000 + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISVolumeSdpUpdateConfig(name, iops1, capacity1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISVolumeExists("ibm_is_volume.storage", vol), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "name", name), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "capacity", fmt.Sprintf("%d", capacity1)), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "iops", fmt.Sprintf("%d", iops1)), + ), + }, + { + Config: testAccCheckIBMISVolumeSdpUpdateConfig(name1, iops1, capacity1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISVolumeExists("ibm_is_volume.storage", vol), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "name", name1), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "capacity", fmt.Sprintf("%d", capacity1)), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "iops", fmt.Sprintf("%d", iops1)), + ), + }, + { + Config: testAccCheckIBMISVolumeSdpUpdateConfig(name1, iops1, capacity2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISVolumeExists("ibm_is_volume.storage", vol), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "name", name1), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "capacity", fmt.Sprintf("%d", capacity2)), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "iops", fmt.Sprintf("%d", iops1)), + ), + }, + + { + Config: testAccCheckIBMISVolumeSdpUpdateConfig(name1, iops2, capacity2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISVolumeExists("ibm_is_volume.storage", vol), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "name", name1), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "capacity", fmt.Sprintf("%d", capacity2)), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "iops", fmt.Sprintf("%d", iops2)), + ), + }, + }, + }) +} func TestAccIBMISVolume_snapshot(t *testing.T) { var vol string @@ -373,15 +473,41 @@ func testAccCheckIBMISVolumeConfig(name string) string { return fmt.Sprintf( ` resource "ibm_is_volume" "storage"{ - name = "%s" - profile = "10iops-tier" - zone = "us-south-1" + name = "%s" + profile = "10iops-tier" + zone = "us-south-1" # capacity= 200 } `, name) } +func testAccCheckIBMISVolumeSdpConfig(name string, capacity int) string { + return fmt.Sprintf( + ` + resource "ibm_is_volume" "storage"{ + name = "%s" + profile = "sdp" + zone = "eu-gb-1" + capacity = %d + } +`, name, capacity) + +} +func testAccCheckIBMISVolumeSdpUpdateConfig(name string, iops, capacity int) string { + return fmt.Sprintf( + ` + resource "ibm_is_volume" "storage"{ + name = "%s" + profile = "sdp" + iops = %d + zone = "eu-gb-1" + capacity = %d + } +`, name, iops, capacity) + +} + func testAccCheckIBMISVolumeCustomConfig(vpcname, subnetname, sshname, publicKey, name, volName string, iops int64) string { return fmt.Sprintf( ` diff --git a/website/docs/d/is_volume.html.markdown b/website/docs/d/is_volume.html.markdown index b5389962ac..1d85cae95b 100644 --- a/website/docs/d/is_volume.html.markdown +++ b/website/docs/d/is_volume.html.markdown @@ -46,6 +46,8 @@ In addition to all argument reference list, you can access the following attribu - `access_tags` - (List) Access management tags associated for the volume. - `active` - (Boolean) Indicates whether a running virtual server instance has an attachment to this volume. - `attachment_state` - (Boolean) The attachment state of the volume +- `adjustable_capacity_states` - (List) The attachment states that support adjustable capacity for this volume. Allowable list items are: `attached`, `unattached`, `unusable`. +- `adjustable_iops_states` - (List) The attachment states that support adjustable IOPS for this volume. Allowable list items are: `attached`, `unattached`, `unusable`. - `bandwidth` - The maximum bandwidth (in megabits per second) for the volume - `busy` - (Boolean) Indicates whether this volume is performing an operation that must be serialized. This must be `false` to perform an operation that is specified to require serialization. - `capacity` - (String) The capacity of the volume in gigabytes. @@ -91,3 +93,5 @@ In addition to all argument reference list, you can access the following attribu - `message` - (String) An explanation of the status reason - `more_info` - (String) Link to documentation about this status reason - `tags` - (String) User Tags associated with the volume. (https://cloud.ibm.com/apidocs/tagging#types-of-tags) +- `unattached_capacity_update_supported` - (Boolean) Indicates whether the capacity for the volume can be changed when not attached to a running virtual server instance. +- `unattached_iops_update_supported` - (Boolean) Indicates whether the IOPS for the volume can be changed when not attached to a running virtual server instance. diff --git a/website/docs/d/is_volume_profile.html.markdown b/website/docs/d/is_volume_profile.html.markdown index 8cdf6c9683..4f098dfcbe 100644 --- a/website/docs/d/is_volume_profile.html.markdown +++ b/website/docs/d/is_volume_profile.html.markdown @@ -38,4 +38,13 @@ Review the argument references that you can specify for your data source. ## Attribute reference In addition to the argument reference list, you can access the following attribute references after your data source is created. +- `adjustable_capacity_states` - (List) + Nested schema for **adjustable_capacity_states**: + - `type` - (String) The type for this profile field. + - `values` - (List) The attachment states that support adjustable capacity for a volume with this profile. Allowable list items are: `attached`, `unattached`, `unusable`. +- `adjustable_iops_states` - (List) + Nested schema for **adjustable_iops_states**: + - `type` - (String) The type for this profile field. + - `values` - (List) The attachment states that support adjustable IOPS for a volume with this profile. Allowable list items are: `attached`, `unattached`, `unusable`. - `family` - (String) The family of the virtual server volume profile. + diff --git a/website/docs/d/is_volume_profiles.html.markdown b/website/docs/d/is_volume_profiles.html.markdown index 77efa299f2..12c6c3e8cf 100644 --- a/website/docs/d/is_volume_profiles.html.markdown +++ b/website/docs/d/is_volume_profiles.html.markdown @@ -35,6 +35,14 @@ You can access the following attribute references after your data source is crea - `profiles` - (List) Lists all server volume profiles in the region. Nested scheme for `profiles`: + - `adjustable_capacity_states` - (List) + Nested schema for **adjustable_capacity_states**: + - `type` - (String) The type for this profile field. + - `values` - (List) The attachment states that support adjustable capacity for a volume with this profile. Allowable list items are: `attached`, `unattached`, `unusable`. + - `adjustable_iops_states` - (List) + Nested schema for **adjustable_iops_states**: + - `type` - (String) The type for this profile field. + - `values` - (List) The attachment states that support adjustable IOPS for a volume with this profile. Allowable list items are: `attached`, `unattached`, `unusable`. - `name` - (String) The name of the virtual server volume profile. - `family` - (String) The family of the virtual server volume profile. diff --git a/website/docs/d/is_volumes.html.markdown b/website/docs/d/is_volumes.html.markdown index 1a8dd26481..609bd268be 100644 --- a/website/docs/d/is_volumes.html.markdown +++ b/website/docs/d/is_volumes.html.markdown @@ -45,6 +45,8 @@ In addition to all argument references listed, you can access the following attr Nested scheme for **volumes**: - `access_tags` - (List) Access management tags associated for the volume. - `active` - (Boolean) Indicates whether a running virtual server instance has an attachment to this volume. + - `adjustable_capacity_states` - (List) The attachment states that support adjustable capacity for this volume. Allowable list items are: `attached`, `unattached`, `unusable`. + - `adjustable_iops_states` - (List) The attachment states that support adjustable IOPS for this volume. Allowable list items are: `attached`, `unattached`, `unusable`. - `attachment_state` - (Boolean) The attachment state of the volume - `bandwidth` - (Integer) The maximum bandwidth (in megabits per second) for the volume. - `busy` - (Boolean) Indicates whether this volume is performing an operation that must be serialized. This must be `false` to perform an operation that is specified to require serialization. diff --git a/website/docs/r/is_volume.html.markdown b/website/docs/r/is_volume.html.markdown index 1b909fe257..cc26dc7eb6 100644 --- a/website/docs/r/is_volume.html.markdown +++ b/website/docs/r/is_volume.html.markdown @@ -71,6 +71,8 @@ Review the argument references that you can specify for your resource. **•** For more information, about creating access tags, see [working with tags](https://cloud.ibm.com/docs/account?topic=account-tag&interface=ui#create-access-console).
**•** You must have the access listed in the [Granting users access to tag resources](https://cloud.ibm.com/docs/account?topic=account-access) for `access_tags`
**•** `access_tags` must be in the format `key:value`. +- `adjustable_capacity_states` - (List) The attachment states that support adjustable capacity for this volume. Allowable list items are: `attached`, `unattached`, `unusable`. +- `adjustable_iops_states` - (List) The attachment states that support adjustable IOPS for this volume. Allowable list items are: `attached`, `unattached`, `unusable`. - `capacity` - (Optional, Integer) (The capacity of the volume in gigabytes. This defaults to `100`, minimum to `10 ` and maximum to `16000`. ~> **NOTE:** Supports only expansion on update (must be attached to a running instance and must not be less than the current volume capacity). Can be updated only if volume is attached to an running virtual server instance. Stopped instance will be started on update of capacity of the volume.If `source_snapshot` is provided `capacity` must be at least the snapshot's minimum_capacity. The maximum value may increase in the future and If unspecified, the capacity will be the source snapshot's minimum_capacity.