From 761edc4a6b4e515fcadf95dc44e25c0b82e8482d Mon Sep 17 00:00:00 2001 From: Deepak Selvakumar Date: Thu, 24 Jun 2021 13:24:16 +0530 Subject: [PATCH] Feature: instance bandwidth --- ibm/data_source_ibm_is_instance.go | 30 +++++ ibm/data_source_ibm_is_instance_profile.go | 97 ++++++++++++++- ibm/data_source_ibm_is_instance_profiles.go | 59 ++++++++- ibm/data_source_ibm_is_instance_template.go | 13 ++ ibm/data_source_ibm_is_instance_templates.go | 9 ++ ibm/data_source_ibm_is_instances.go | 28 +++++ ibm/data_source_ibm_is_volume.go | 7 ++ ibm/resource_ibm_is_instance.go | 77 +++++++++++- ibm/resource_ibm_is_instance_template.go | 24 ++++ ibm/resource_ibm_is_instance_test.go | 117 ++++++++++++++++++ ibm/resource_ibm_is_volume.go | 8 ++ website/docs/d/is_instance.html.markdown | 3 + .../docs/d/is_instance_profile.html.markdown | 8 ++ .../docs/d/is_instance_profiles.html.markdown | 9 ++ .../docs/d/is_instance_template.html.markdown | 1 + .../d/is_instance_templates.html.markdown | 3 +- website/docs/d/is_instances.html.markdown | 3 + website/docs/d/is_volume.html.markdown | 1 + website/docs/r/is_instance.html.markdown | 3 + .../docs/r/is_instance_template.html.markdown | 1 + website/docs/r/is_volume.html.markdown | 1 + 21 files changed, 492 insertions(+), 10 deletions(-) diff --git a/ibm/data_source_ibm_is_instance.go b/ibm/data_source_ibm_is_instance.go index 1c9ce61666..54d531c459 100644 --- a/ibm/data_source_ibm_is_instance.go +++ b/ibm/data_source_ibm_is_instance.go @@ -98,6 +98,24 @@ func dataSourceIBMISInstance() *schema.Resource { Description: "Profile info", }, + isInstanceTotalVolumeBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes", + }, + + isInstanceBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The total bandwidth (in megabits per second) shared across the instance's network interfaces and storage volumes", + }, + + isInstanceTotalNetworkBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance network interfaces.", + }, + isInstanceTags: { Type: schema.TypeSet, Computed: true, @@ -561,6 +579,18 @@ func instanceGetByName(d *schema.ResourceData, meta interface{}, name string) er d.Set(isInstanceGpu, gpuList) } + if instance.Bandwidth != nil { + d.Set(isInstanceBandwidth, int(*instance.Bandwidth)) + } + + if instance.TotalNetworkBandwidth != nil { + d.Set(isInstanceTotalNetworkBandwidth, int(*instance.TotalNetworkBandwidth)) + } + + if instance.TotalVolumeBandwidth != nil { + d.Set(isInstanceTotalVolumeBandwidth, int(*instance.TotalVolumeBandwidth)) + } + if instance.Disks != nil { d.Set(isInstanceDisks, dataSourceInstanceFlattenDisks(instance.Disks)) } diff --git a/ibm/data_source_ibm_is_instance_profile.go b/ibm/data_source_ibm_is_instance_profile.go index 4b3d7e2747..8972880741 100644 --- a/ibm/data_source_ibm_is_instance_profile.go +++ b/ibm/data_source_ibm_is_instance_profile.go @@ -101,7 +101,7 @@ func dataSourceIBMISInstanceProfile() *schema.Resource { "gpu_count": { Type: schema.TypeList, Computed: true, - Description: "Collection of the instance profile's disks.", + Description: "GPU count of this profile", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": &schema.Schema{ @@ -148,7 +148,7 @@ func dataSourceIBMISInstanceProfile() *schema.Resource { "gpu_manufacturer": { Type: schema.TypeList, Computed: true, - Description: "Collection of the instance profile's disks.", + Description: "GPU manufacturer of this profile", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": &schema.Schema{ @@ -170,7 +170,7 @@ func dataSourceIBMISInstanceProfile() *schema.Resource { "gpu_memory": { Type: schema.TypeList, Computed: true, - Description: "Collection of the instance profile's disks.", + Description: "GPU memory of this profile", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": &schema.Schema{ @@ -217,7 +217,7 @@ func dataSourceIBMISInstanceProfile() *schema.Resource { "gpu_model": { Type: schema.TypeList, Computed: true, - Description: "Collection of the instance profile's disks.", + Description: "GPU model of this profile", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": &schema.Schema{ @@ -236,6 +236,53 @@ func dataSourceIBMISInstanceProfile() *schema.Resource { }, }, }, + "total_volume_bandwidth": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes. An increase in this value will result in a corresponding decrease to total_network_bandwidth.", + 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, + }, + }, + }, + }, + }, "disks": &schema.Schema{ Type: schema.TypeList, Computed: true, @@ -573,6 +620,12 @@ func instanceProfileGet(d *schema.ResourceData, meta interface{}, name string) e return err } } + if profile.TotalVolumeBandwidth != nil { + err = d.Set("total_volume_bandwidth", dataSourceInstanceProfileFlattenTotalVolumeBandwidth(*profile.TotalVolumeBandwidth.(*vpcv1.InstanceProfileVolumeBandwidth))) + if err != nil { + return err + } + } if profile.Disks != nil { err = d.Set("disks", dataSourceInstanceProfileFlattenDisks(profile.Disks)) if err != nil { @@ -983,3 +1036,39 @@ func dataSourceInstanceProfileDisksSupportedInterfaceTypesToMap(supportedInterfa return supportedInterfaceTypesMap } + +func dataSourceInstanceProfileFlattenTotalVolumeBandwidth(result vpcv1.InstanceProfileVolumeBandwidth) (finalList []map[string]interface{}) { + finalList = []map[string]interface{}{} + finalMap := dataSourceInstanceProfileTotalVolumeBandwidthToMap(result) + finalList = append(finalList, finalMap) + + return finalList +} + +func dataSourceInstanceProfileTotalVolumeBandwidthToMap(bandwidthItem vpcv1.InstanceProfileVolumeBandwidth) (bandwidthMap map[string]interface{}) { + bandwidthMap = map[string]interface{}{} + + if bandwidthItem.Type != nil { + bandwidthMap["type"] = bandwidthItem.Type + } + if bandwidthItem.Value != nil { + bandwidthMap["value"] = bandwidthItem.Value + } + if bandwidthItem.Default != nil { + bandwidthMap["default"] = bandwidthItem.Default + } + if bandwidthItem.Max != nil { + bandwidthMap["max"] = bandwidthItem.Max + } + if bandwidthItem.Min != nil { + bandwidthMap["min"] = bandwidthItem.Min + } + if bandwidthItem.Step != nil { + bandwidthMap["step"] = bandwidthItem.Step + } + if bandwidthItem.Values != nil { + bandwidthMap["values"] = bandwidthItem.Values + } + + return bandwidthMap +} diff --git a/ibm/data_source_ibm_is_instance_profiles.go b/ibm/data_source_ibm_is_instance_profiles.go index 09f9bb7642..013326071f 100644 --- a/ibm/data_source_ibm_is_instance_profiles.go +++ b/ibm/data_source_ibm_is_instance_profiles.go @@ -104,7 +104,7 @@ func dataSourceIBMISInstanceProfiles() *schema.Resource { "gpu_count": { Type: schema.TypeList, Computed: true, - Description: "Collection of the instance profile's disks.", + Description: "GPU count of this profile", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": &schema.Schema{ @@ -151,7 +151,7 @@ func dataSourceIBMISInstanceProfiles() *schema.Resource { "gpu_manufacturer": { Type: schema.TypeList, Computed: true, - Description: "Collection of the instance profile's disks.", + Description: "GPU manufacturer of this profile", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": &schema.Schema{ @@ -173,7 +173,7 @@ func dataSourceIBMISInstanceProfiles() *schema.Resource { "gpu_memory": { Type: schema.TypeList, Computed: true, - Description: "Collection of the instance profile's disks.", + Description: "GPU memory of this profile", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": &schema.Schema{ @@ -220,7 +220,7 @@ func dataSourceIBMISInstanceProfiles() *schema.Resource { "gpu_model": { Type: schema.TypeList, Computed: true, - Description: "Collection of the instance profile's disks.", + Description: "GPU model of this profile", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": &schema.Schema{ @@ -239,6 +239,53 @@ func dataSourceIBMISInstanceProfiles() *schema.Resource { }, }, }, + "total_volume_bandwidth": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes. An increase in this value will result in a corresponding decrease to total_network_bandwidth.", + 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, + }, + }, + }, + }, + }, "disks": &schema.Schema{ Type: schema.TypeList, Computed: true, @@ -570,6 +617,10 @@ func instanceProfilesList(d *schema.ResourceData, meta interface{}) error { l["gpu_model"] = dataSourceInstanceProfileFlattenGPUModel(*profile.GpuModel) } + if profile.TotalVolumeBandwidth != nil { + l["total_volume_bandwidth"] = dataSourceInstanceProfileFlattenTotalVolumeBandwidth(*profile.TotalVolumeBandwidth.(*vpcv1.InstanceProfileVolumeBandwidth)) + } + if profile.Disks != nil { disksList := []map[string]interface{}{} for _, disksItem := range profile.Disks { diff --git a/ibm/data_source_ibm_is_instance_template.go b/ibm/data_source_ibm_is_instance_template.go index b15d3f9e78..85d30bd25b 100644 --- a/ibm/data_source_ibm_is_instance_template.go +++ b/ibm/data_source_ibm_is_instance_template.go @@ -96,6 +96,11 @@ func dataSourceIBMISInstanceTemplate() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + isInstanceTotalVolumeBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes", + }, isInstanceTemplateVolumeAttachments: { Type: schema.TypeList, Computed: true, @@ -307,6 +312,10 @@ func dataSourceIBMISInstanceTemplateRead(context context.Context, d *schema.Reso d.Set("placement_target", placementTargetList) } + if instance.TotalVolumeBandwidth != nil { + d.Set(isInstanceTotalVolumeBandwidth, int(*instance.TotalVolumeBandwidth)) + } + if instance.PrimaryNetworkInterface != nil { interfaceList := make([]map[string]interface{}, 0) currentPrimNic := map[string]interface{}{} @@ -528,6 +537,10 @@ func dataSourceIBMISInstanceTemplateRead(context context.Context, d *schema.Reso d.Set(isInstanceTemplateNetworkInterfaces, interfacesList) } + if instance.TotalVolumeBandwidth != nil { + d.Set(isInstanceTotalVolumeBandwidth, int(*instance.TotalVolumeBandwidth)) + } + if instance.Image != nil { imageInf := instance.Image imageIdentity := imageInf.(*vpcv1.ImageIdentity) diff --git a/ibm/data_source_ibm_is_instance_templates.go b/ibm/data_source_ibm_is_instance_templates.go index 43845fbe50..0e5b036af1 100644 --- a/ibm/data_source_ibm_is_instance_templates.go +++ b/ibm/data_source_ibm_is_instance_templates.go @@ -124,6 +124,11 @@ func dataSourceIBMISInstanceTemplates() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + isInstanceTotalVolumeBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes", + }, isInstanceTemplateVolumeAttachments: { Type: schema.TypeList, Computed: true, @@ -344,6 +349,10 @@ func dataSourceIBMISInstanceTemplatesRead(d *schema.ResourceData, meta interface template["placement_target"] = placementTargetList } + if instance.TotalVolumeBandwidth != nil { + template[isInstanceTotalVolumeBandwidth] = int(*instance.TotalVolumeBandwidth) + } + if instance.PrimaryNetworkInterface != nil { interfaceList := make([]map[string]interface{}, 0) currentPrimNic := map[string]interface{}{} diff --git a/ibm/data_source_ibm_is_instances.go b/ibm/data_source_ibm_is_instances.go index 72cd886daa..944caee59c 100644 --- a/ibm/data_source_ibm_is_instances.go +++ b/ibm/data_source_ibm_is_instances.go @@ -346,6 +346,23 @@ func dataSourceIBMISInstances() *schema.Resource { Computed: true, Description: "Instance Profile", }, + isInstanceTotalVolumeBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes", + }, + + isInstanceBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The total bandwidth (in megabits per second) shared across the instance's network interfaces and storage volumes", + }, + + isInstanceTotalNetworkBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance network interfaces.", + }, "vcpu": { Type: schema.TypeList, Computed: true, @@ -642,6 +659,17 @@ func instancesList(d *schema.ResourceData, meta interface{}) error { placementTargetMap := resourceIbmIsInstanceInstancePlacementToMap(*instance.PlacementTarget.(*vpcv1.InstancePlacementTarget)) l["placement_target"] = []map[string]interface{}{placementTargetMap} } + if instance.Bandwidth != nil { + l[isInstanceBandwidth] = int(*instance.Bandwidth) + } + + if instance.TotalNetworkBandwidth != nil { + l[isInstanceTotalNetworkBandwidth] = int(*instance.TotalNetworkBandwidth) + } + + if instance.TotalVolumeBandwidth != nil { + l[isInstanceTotalVolumeBandwidth] = int(*instance.TotalVolumeBandwidth) + } if instance.BootVolumeAttachment != nil { bootVolList := make([]map[string]interface{}, 0) diff --git a/ibm/data_source_ibm_is_volume.go b/ibm/data_source_ibm_is_volume.go index ef6e710483..48f5e0ff32 100644 --- a/ibm/data_source_ibm_is_volume.go +++ b/ibm/data_source_ibm_is_volume.go @@ -31,6 +31,12 @@ func dataSourceIBMISVolume() *schema.Resource { Description: "Zone name", }, + isVolumeBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The maximum bandwidth (in megabits per second) for the volume", + }, + isVolumeResourceGroup: { Type: schema.TypeString, Computed: true, @@ -201,6 +207,7 @@ func volumeGet(d *schema.ResourceData, meta interface{}, name string) error { } for _, vol := range allrecs { d.SetId(*vol.ID) + d.Set(isVolumeBandwidth, int(*vol.Bandwidth)) d.Set(isVolumeName, *vol.Name) d.Set(isVolumeProfileName, *vol.Profile.Name) d.Set(isVolumeZone, *vol.Zone.Name) diff --git a/ibm/resource_ibm_is_instance.go b/ibm/resource_ibm_is_instance.go index 883f18ecfa..0e7763d477 100644 --- a/ibm/resource_ibm_is_instance.go +++ b/ibm/resource_ibm_is_instance.go @@ -39,6 +39,9 @@ const ( isInstanceBootVolume = "boot_volume" isInstanceVolumeSnapshot = "snapshot" isInstanceSourceTemplate = "instance_template" + isInstanceBandwidth = "bandwidth" + isInstanceTotalVolumeBandwidth = "total_volume_bandwidth" + isInstanceTotalNetworkBandwidth = "total_network_bandwidth" isInstanceVolAttVolAutoDelete = "auto_delete_volume" isInstanceVolAttVolBillingTerm = "billing_term" isInstanceImage = "image" @@ -212,6 +215,26 @@ func resourceIBMISInstance() *schema.Resource { Description: "Unique Identifier of the Placement Group for restricting the placement of the instance", }, + isInstanceTotalVolumeBandwidth: { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: InvokeValidator("ibm_is_instance", isInstanceTotalVolumeBandwidth), + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes", + }, + + isInstanceBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The total bandwidth (in megabits per second) shared across the instance's network interfaces and storage volumes", + }, + + isInstanceTotalNetworkBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance network interfaces.", + }, + isInstanceKeys: { Type: schema.TypeSet, Optional: true, @@ -675,6 +698,13 @@ func resourceIBMISInstanceValidator() *ResourceValidator { Regexp: `^[A-Za-z0-9:_ .-]+$`, MinValueLength: 1, MaxValueLength: 128}) + validateSchema = append(validateSchema, + ValidateSchema{ + Identifier: isInstanceTotalVolumeBandwidth, + ValidateFunctionIdentifier: IntAtLeast, + Type: TypeInt, + Optional: true, + MinValue: "500"}) validateSchema = append(validateSchema, ValidateSchema{ @@ -710,7 +740,10 @@ func instanceCreateByImage(d *schema.ResourceData, meta interface{}, profile, na ID: &vpcID, }, } - + if totalVolBandwidthIntf, ok := d.GetOk(isInstanceTotalVolumeBandwidth); ok { + totalVolBandwidthStr := int64(totalVolBandwidthIntf.(int)) + instanceproto.TotalVolumeBandwidth = &totalVolBandwidthStr + } if dHostIdInf, ok := d.GetOk(isPlacementTargetDedicatedHost); ok { dHostIdStr := dHostIdInf.(string) dHostPlaementTarget := &vpcv1.InstancePlacementTargetPrototypeDedicatedHostIdentity{ @@ -917,6 +950,11 @@ func instanceCreateByTemplate(d *schema.ResourceData, meta interface{}, profile, Name: &profile, } } + if totalVolBandwidthIntf, ok := d.GetOk(isInstanceTotalVolumeBandwidth); ok { + totalVolBandwidthStr := int64(totalVolBandwidthIntf.(int)) + instanceproto.TotalVolumeBandwidth = &totalVolBandwidthStr + } + if vpcID != "" { instanceproto.VPC = &vpcv1.VPCIdentity{ ID: &vpcID, @@ -1189,6 +1227,10 @@ func instanceCreateByVolume(d *schema.ResourceData, meta interface{}, profile, n Volume: volTemplate, } } + if totalVolBandwidthIntf, ok := d.GetOk(isInstanceTotalVolumeBandwidth); ok { + totalVolBandwidthStr := int64(totalVolBandwidthIntf.(int)) + instanceproto.TotalVolumeBandwidth = &totalVolBandwidthStr + } if primnicintf, ok := d.GetOk(isInstancePrimaryNetworkInterface); ok { primnic := primnicintf.([]interface{})[0].(map[string]interface{}) @@ -1494,6 +1536,18 @@ func instanceGet(d *schema.ResourceData, meta interface{}, id string) error { } d.Set(isInstanceCPU, cpuList) + if instance.Bandwidth != nil { + d.Set(isInstanceBandwidth, int(*instance.Bandwidth)) + } + + if instance.TotalNetworkBandwidth != nil { + d.Set(isInstanceTotalNetworkBandwidth, int(*instance.TotalNetworkBandwidth)) + } + + if instance.TotalVolumeBandwidth != nil { + d.Set(isInstanceTotalVolumeBandwidth, int(*instance.TotalVolumeBandwidth)) + } + d.Set(isInstanceMemory, *instance.Memory) gpuList := make([]map[string]interface{}, 0) if instance.Gpu != nil { @@ -1897,6 +1951,27 @@ func instanceUpdate(d *schema.ResourceData, meta interface{}) error { } + if d.HasChange(isInstanceTotalVolumeBandwidth) && !d.IsNewResource() { + totalVolBandwidth := int64(d.Get(isInstanceTotalVolumeBandwidth).(int)) + updnetoptions := &vpcv1.UpdateInstanceOptions{ + ID: &id, + } + + instancePatchModel := &vpcv1.InstancePatch{ + TotalVolumeBandwidth: &totalVolBandwidth, + } + instancePatch, err := instancePatchModel.AsPatch() + if err != nil { + return fmt.Errorf("Error calling asPatch with total volume bandwidth for InstancePatch: %s", err) + } + updnetoptions.InstancePatch = instancePatch + + _, _, err = instanceC.UpdateInstance(updnetoptions) + if err != nil { + return err + } + } + if d.HasChange(isInstanceName) && !d.IsNewResource() { name := d.Get(isInstanceName).(string) updnetoptions := &vpcv1.UpdateInstanceOptions{ diff --git a/ibm/resource_ibm_is_instance_template.go b/ibm/resource_ibm_is_instance_template.go index 428f03cdb3..1d7a1d5986 100644 --- a/ibm/resource_ibm_is_instance_template.go +++ b/ibm/resource_ibm_is_instance_template.go @@ -99,6 +99,14 @@ func resourceIBMISInstanceTemplate() *schema.Resource { Description: "Profile info", }, + isInstanceTotalVolumeBandwidth: { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: InvokeValidator("ibm_is_instance_template", isInstanceTotalVolumeBandwidth), + Description: "The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes", + }, + isInstanceTemplateKeys: { Type: schema.TypeSet, Required: true, @@ -372,6 +380,13 @@ func resourceIBMISInstanceTemplateValidator() *ResourceValidator { Regexp: `^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$`, MinValueLength: 1, MaxValueLength: 63}) + validateSchema = append(validateSchema, + ValidateSchema{ + Identifier: isInstanceTotalVolumeBandwidth, + ValidateFunctionIdentifier: IntAtLeast, + Type: TypeInt, + Optional: true, + MinValue: "500"}) ibmISInstanceTemplateValidator := ResourceValidator{ResourceName: "ibm_is_instance_template", Schema: validateSchema} return &ibmISInstanceTemplateValidator @@ -475,6 +490,11 @@ func instanceTemplateCreate(d *schema.ResourceData, meta interface{}, profile, n instanceproto.PlacementTarget = placementGrp } + if totalVolBandwidthIntf, ok := d.GetOk(isInstanceTotalVolumeBandwidth); ok { + totalVolBandwidthStr := int64(totalVolBandwidthIntf.(int)) + instanceproto.TotalVolumeBandwidth = &totalVolBandwidthStr + } + // BOOT VOLUME ATTACHMENT for instance template if boot, ok := d.GetOk(isInstanceTemplateBootVolume); ok { bootvol := boot.([]interface{})[0].(map[string]interface{}) @@ -716,6 +736,10 @@ func instanceTemplateGet(d *schema.ResourceData, meta interface{}, ID string) er d.Set(isInstanceTemplateProfile, *identity.Name) } + if instance.TotalVolumeBandwidth != nil { + d.Set(isInstanceTotalVolumeBandwidth, int(*instance.TotalVolumeBandwidth)) + } + var placementTargetMap map[string]interface{} if instance.PlacementTarget != nil { placementTargetMap = resourceIbmIsInstanceTemplateInstancePlacementTargetPrototypeToMap(*instance.PlacementTarget.(*vpcv1.InstancePlacementTargetPrototype)) diff --git a/ibm/resource_ibm_is_instance_test.go b/ibm/resource_ibm_is_instance_test.go index e8cac9eb98..6ae3bcae09 100644 --- a/ibm/resource_ibm_is_instance_test.go +++ b/ibm/resource_ibm_is_instance_test.go @@ -6,6 +6,7 @@ package ibm import ( "errors" "fmt" + "strconv" "strings" "testing" @@ -44,6 +45,50 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE }) } +func TestAccIBMISInstanceBandwidth_basic(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)) + totalVolumeBandwidth := 1000 + totalVolumeBandwidthUpdated := 1000 + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIBMISInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISInstanceBandwidthConfig(vpcname, subnetname, sshname, publicKey, name, totalVolumeBandwidth), + 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", "zone", ISZoneName), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "total_volume_bandwidth", strconv.Itoa(totalVolumeBandwidth)), + ), + }, + { + Config: testAccCheckIBMISInstanceBandwidthUpdateConfig(vpcname, subnetname, sshname, publicKey, name, totalVolumeBandwidthUpdated), + 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", "zone", ISZoneName), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "total_volume_bandwidth", strconv.Itoa(totalVolumeBandwidthUpdated)), + ), + }, + }, + }) +} + func TestAccIBMISInstanceWithSecurityGroup_basic(t *testing.T) { var instance string vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) @@ -429,6 +474,78 @@ func testAccCheckIBMISInstanceConfig(vpcname, subnetname, sshname, publicKey, na }`, vpcname, subnetname, ISZoneName, ISCIDR, sshname, publicKey, name, isImage, instanceProfileName, ISZoneName) } +func testAccCheckIBMISInstanceBandwidthConfig(vpcname, subnetname, sshname, publicKey, name string, bandwidth int) 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" + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + total_volume_bandwidth = %d + 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" + } + }`, vpcname, subnetname, ISZoneName, ISCIDR, sshname, publicKey, name, isImage, instanceProfileName, bandwidth, ISZoneName) +} + +func testAccCheckIBMISInstanceBandwidthUpdateConfig(vpcname, subnetname, sshname, publicKey, name string, bandwidth int) 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" + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + total_volume_bandwidth = %d + 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" + } + }`, vpcname, subnetname, ISZoneName, ISCIDR, sshname, publicKey, name, isImage, instanceProfileName, bandwidth, ISZoneName) +} + func testAccCheckIBMISInstanceWithSecurityGroupConfig(vpcname, subnetname, sshname, publicKey, secgrpname, name string) string { return fmt.Sprintf(` resource "ibm_is_vpc" "testacc_vpc" { diff --git a/ibm/resource_ibm_is_volume.go b/ibm/resource_ibm_is_volume.go index b5105fd640..c5e730f8a2 100644 --- a/ibm/resource_ibm_is_volume.go +++ b/ibm/resource_ibm_is_volume.go @@ -37,6 +37,7 @@ const ( isVolumeResourceGroup = "resource_group" isVolumeSourceSnapshot = "source_snapshot" isVolumeDeleteAllSnapshots = "delete_all_snapshots" + isVolumeBandwidth = "bandwidth" ) func resourceIBMISVolume() *schema.Resource { @@ -202,6 +203,12 @@ func resourceIBMISVolume() *schema.Resource { Computed: true, Description: "The resource group name in which resource is provisioned", }, + + isVolumeBandwidth: { + Type: schema.TypeInt, + Computed: true, + Description: "The maximum bandwidth (in megabits per second) for the volume", + }, }, } } @@ -378,6 +385,7 @@ func volGet(d *schema.ResourceData, meta interface{}, id string) error { d.Set(isVolumeSourceSnapshot, *vol.SourceSnapshot.ID) } d.Set(isVolumeStatus, *vol.Status) + d.Set(isVolumeBandwidth, int(*vol.Bandwidth)) //set the status reasons if vol.StatusReasons != nil { statusReasonsList := make([]map[string]interface{}, 0) diff --git a/website/docs/d/is_instance.html.markdown b/website/docs/d/is_instance.html.markdown index 78cb7bb40e..71f4837785 100644 --- a/website/docs/d/is_instance.html.markdown +++ b/website/docs/d/is_instance.html.markdown @@ -65,6 +65,7 @@ Review the argument references that you can specify for your data source. ## Attribute reference In addition to all argument reference list, you can access the following attribute references after your data source is created. +- `bandwidth` - (Integer) The total bandwidth (in megabits per second) shared across the instance's network interfaces and storage volumes - `boot_volume` - (List of Objects) A list of boot volumes that were created for the instance. Nested scheme for `boot_volume`: @@ -134,6 +135,8 @@ In addition to all argument reference list, you can access the following attribu Nested scheme for `status_reasons`: - `code` - (String) A snake case string identifying the status reason. - `message` - (String) An explanation of the status reason +- `total_volume_bandwidth` - (Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes +- `total_network_bandwidth` - (Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance network interfaces. - `vpc` - (String) The ID of the VPC that the instance belongs to. - `vcpu`- (List) A list of virtual CPUs that were allocated to the instance. diff --git a/website/docs/d/is_instance_profile.html.markdown b/website/docs/d/is_instance_profile.html.markdown index 54fccfc571..a2db2e5096 100644 --- a/website/docs/d/is_instance_profile.html.markdown +++ b/website/docs/d/is_instance_profile.html.markdown @@ -42,6 +42,14 @@ In addition to the argument reference list, you can access the following attribu - `type` - (String) The type for this profile field. - `value` - (String) The value for this profile field. - `values` - (String) The permitted values for this profile field. +- `total_volume_bandwidth` Nested `total_volume_bandwidth` blocks have the following structure: + - `type` - The type for this profile field. + - `value` - The value for this profile field. + - `default` - The default value for this profile field. + - `max` - The maximum value for this profile field. + - `min` - The minimum value for this profile field. + - `step` - The increment step value for this profile field. + - `values` - The permitted values for this profile field. - `disks` - (List) Collection of the instance profile's disks. Nested `disks` blocks have the following structure: Nested scheme for `disks`: diff --git a/website/docs/d/is_instance_profiles.html.markdown b/website/docs/d/is_instance_profiles.html.markdown index fae6cc5745..cca49db979 100644 --- a/website/docs/d/is_instance_profiles.html.markdown +++ b/website/docs/d/is_instance_profiles.html.markdown @@ -95,6 +95,15 @@ You can access the following attribute references after your data source is crea Nested scheme for `gpu_model`: - `type` - (String) The type for this profile field. - `values` - (String) The permitted values for this profile field. + - `total_volume_bandwidth` Nested `total_volume_bandwidth` blocks have the following structure: + Nested scheme for `total_volume_bandwidth`: + - `type` - The type for this profile field. + - `value` - The value for this profile field. + - `default` - The default value for this profile field. + - `max` - The maximum value for this profile field. + - `min` - The minimum value for this profile field. + - `step` - The increment step value for this profile field. + - `values` - The permitted values for this profile field. - `href` - (String) The URL for this virtual server instance profile. - `memory` - (List) Nested `memory` blocks have the following structure: diff --git a/website/docs/d/is_instance_template.html.markdown b/website/docs/d/is_instance_template.html.markdown index a22f972bda..9f66efc6b3 100644 --- a/website/docs/d/is_instance_template.html.markdown +++ b/website/docs/d/is_instance_template.html.markdown @@ -74,6 +74,7 @@ You can access the following attribute references after your data source is crea - `subnet` - (String) The VPC subnet to assign to the interface. - `security_groups` - (String) List of security groups of the subnet. - `resource_group` - (String) The resource group ID. +- `total_volume_bandwidth` - (Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes - `user_data` - (String) The user data provided for the instance. - `volume_attachments` - (List) A nested block describes the storage volume configuration for the template. diff --git a/website/docs/d/is_instance_templates.html.markdown b/website/docs/d/is_instance_templates.html.markdown index 224e16489d..ff1d8360fa 100644 --- a/website/docs/d/is_instance_templates.html.markdown +++ b/website/docs/d/is_instance_templates.html.markdown @@ -57,7 +57,8 @@ You can access the following attribute references after your data source is crea - `primary_ipv4_address` - (String) The IPv4 address assigned to the primary network interface. - `subnet` - (String) The VPC subnet to assign to the interface. - `security_groups` - (String) List of security groups of the subnet. - - `resource_group` - (String) The resource group ID. + - `resource_group` - (String) The resource group ID. + - `total_volume_bandwidth` - (Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes - `user_data` - (String) The user data provided for the instance. - `volume_attachments` - (List) A nested block describes the storage volume configuration for the template. diff --git a/website/docs/d/is_instances.html.markdown b/website/docs/d/is_instances.html.markdown index 747e243171..f89feecee8 100644 --- a/website/docs/d/is_instances.html.markdown +++ b/website/docs/d/is_instances.html.markdown @@ -47,6 +47,7 @@ In addition to all argument reference list, you can access the following attribu - `instances`- (List of Object) A list of Virtual Servers for VPC instances that exist in your account. Nested scheme for `instances`: + - `bandwidth` - (Integer) The total bandwidth (in megabits per second) shared across the instance's network interfaces and storage volumes - `boot_volume`- (List) A list of boot volumes that were created for the instance. Nested scheme for `boot_volume`: @@ -107,6 +108,8 @@ In addition to all argument reference list, you can access the following attribu Nested scheme for `status_reasons`: - `code` - (String) A snake case string identifying the status reason. - `message` - (String) An explanation of the status reason + - `total_volume_bandwidth` - (Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes + - `total_network_bandwidth` - (Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance network interfaces. - `volume_attachments`- (List) A list of volume attachments that were created for the instance. Nested scheme for `volume_attachments`: diff --git a/website/docs/d/is_volume.html.markdown b/website/docs/d/is_volume.html.markdown index 39f300444f..7c12181432 100644 --- a/website/docs/d/is_volume.html.markdown +++ b/website/docs/d/is_volume.html.markdown @@ -33,6 +33,7 @@ Review the argument references that you can specify for your data source. ## Attribute reference In addition to all argument reference list, you can access the following attribute references after your data source is created. +- `bandwidth` - The maximum bandwidth (in megabits per second) for the volume - `capacity` - (String) The capacity of the volume in gigabytes. - `crn` - (String) The crn of this volume. - `encryption_key` - (String) The key to use for encrypting this volume. diff --git a/website/docs/r/is_instance.html.markdown b/website/docs/r/is_instance.html.markdown index 55dfa836fd..e6139a9bae 100644 --- a/website/docs/r/is_instance.html.markdown +++ b/website/docs/r/is_instance.html.markdown @@ -306,6 +306,7 @@ Review the argument references that you can specify for your resource. - `instance_template` conflicts with `boot_volume.0.snapshot` - `tags` (Optional, Array of Strings) A list of tags that you want to add to your instance. Tags can help you find your instance more easily later. +- `total_volume_bandwidth` - (Optional, Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes - `user_data` - (Optional, String) User data to transfer to the instance. - `volumes` (Optional, List) A comma separated list of volume IDs to attach to the instance. - `vpc` - (Optional, Forces new resource, String) The ID of the VPC where you want to create the instance. @@ -315,6 +316,7 @@ Review the argument references that you can specify for your resource. ## Attribute reference In addition to all argument reference list, you can access the following attribute reference after your resource is created. +- `bandwidth` - The total bandwidth (in megabits per second) shared across the instance's network interfaces and storage volumes - `boot_volume`- (List of Strings) A list of boot volumes that the instance uses. Nested scheme for `boot_volume`: @@ -375,6 +377,7 @@ In addition to all argument reference list, you can access the following attribu Nested scheme for `status_reasons`: - `code` - (String) A string with an underscore as a special character identifying the status reason. - `message` - (String) An explanation of the status reason. +- `total_network_bandwidth` - (Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance network interfaces. - `volume_attachments`- (List of Strings) A list of volume attachments for the instance. Nested scheme for `volume_attachements`: diff --git a/website/docs/r/is_instance_template.html.markdown b/website/docs/r/is_instance_template.html.markdown index 8b022dabc4..dfc1fdc837 100644 --- a/website/docs/r/is_instance_template.html.markdown +++ b/website/docs/r/is_instance_template.html.markdown @@ -171,6 +171,7 @@ Review the argument references that you can specify for your resource. - `delete_volume_on_instance_delete` - (Optional, Bool) You can configure to delete the boot volume based on instance deletion. - `encryption` - (Optional, String) The encryption key CRN to encrypt the boot volume attached. - `name` - (Optional, String) The name of the boot volume. +- `total_volume_bandwidth` - (Optional, int) The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes - `dedicated_host` - (Optional, Force new resource,String) The placement restrictions to use for the virtual server instance. Unique Identifier of the dedicated host where the instance is placed. **NOTE:** diff --git a/website/docs/r/is_volume.html.markdown b/website/docs/r/is_volume.html.markdown index 4624e512bb..4bb186ead6 100644 --- a/website/docs/r/is_volume.html.markdown +++ b/website/docs/r/is_volume.html.markdown @@ -50,6 +50,7 @@ Review the argument references that you can specify for your resource. - 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. +- `bandwidth` - (Integer) The maximum bandwidth (in megabits per second) for the volume - `delete_all_snapshots` - (Optional, Bool) Deletes all snapshots created from this volume. - `encryption_key` - (Optional, Forces new resource, String) The key to use for encrypting this volume. - `iops` - (Optional, Integer) The total input/ output operations per second (IOPS) for your storage. This value is required for `custom` storage profiles only.