diff --git a/CHANGELOG.md b/CHANGELOG.md
index 019aa2d3..cc2f54bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,8 @@
-## Version 4.10.4
+## Version 4.10.5
### Исправлено
#### kvmvmm
| Идентификатор
задачи | Описание |
| --- | --- |
-| BATF-1220 | Ошибки применения новой конфигурации после импортирования в resource `decort_kvmvm` в cloudapi/kvmvm и в `decort_cb_kvmvm` в cloudbroker/kvmvm |
\ No newline at end of file
+| BATF-1270 | Опциональное поле `iotune` в блоке `disks` в resource `decort_kvmvm` в cloudapi/kvmvm и в resource `decort_cb_kvmvm` в cloudbroker/kvmvm |
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 4ba8df28..28d60669 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ ZIPDIR = ./zip
BINARY=${NAME}
WORKPATH= ./examples/terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAMESPACE}/${VERSION}/${OS_ARCH}
MAINPATH = ./cmd/decort/
-VERSION=4.10.4
+VERSION=4.10.5
OS_ARCH=$(shell go env GOHOSTOS)_$(shell go env GOHOSTARCH)
FILES = ${BINARY}_${VERSION}_darwin_amd64\
diff --git a/docs/resources/cb_kvmvm.md b/docs/resources/cb_kvmvm.md
index a44dfae4..be5995a0 100644
--- a/docs/resources/cb_kvmvm.md
+++ b/docs/resources/cb_kvmvm.md
@@ -190,6 +190,7 @@ Optional:
- `desc` (String) Optional description
- `disk_type` (String) The type of disk in terms of its role in compute: 'B=Boot, D=Data'
- `image_id` (Number) Specify image id for create disk from template
+- `iotune` (Block List, Max: 1) (see [below for nested schema](#nestedblock--disks--iotune))
- `node_ids` (Set of Number)
- `permanently` (Boolean) Disk deletion status
- `pool` (String) Pool name; by default will be chosen automatically
@@ -210,6 +211,26 @@ Read-Only:
- `to_clean` (Boolean)
- `update_time` (Number)
+
+### Nested Schema for `disks.iotune`
+
+Optional:
+
+- `read_bytes_sec` (Number)
+- `read_bytes_sec_max` (Number)
+- `read_iops_sec` (Number)
+- `read_iops_sec_max` (Number)
+- `size_iops_sec` (Number)
+- `total_bytes_sec` (Number)
+- `total_bytes_sec_max` (Number)
+- `total_iops_sec` (Number)
+- `total_iops_sec_max` (Number)
+- `write_bytes_sec` (Number)
+- `write_bytes_sec_max` (Number)
+- `write_iops_sec` (Number)
+- `write_iops_sec_max` (Number)
+
+
### Nested Schema for `libvirt_settings`
diff --git a/docs/resources/kvmvm.md b/docs/resources/kvmvm.md
index 59d9ad2e..220fc806 100644
--- a/docs/resources/kvmvm.md
+++ b/docs/resources/kvmvm.md
@@ -184,6 +184,7 @@ Optional:
- `desc` (String) Optional description
- `disk_type` (String) The type of disk in terms of its role in compute: 'B=Boot, D=Data'
- `image_id` (Number) Specify image id for create disk from template
+- `iotune` (Block List, Max: 1) (see [below for nested schema](#nestedblock--disks--iotune))
- `permanently` (Boolean) Disk deletion status
- `pool` (String) Pool name; by default will be chosen automatically
- `sep_id` (Number) Storage endpoint provider ID; by default the same with boot disk
@@ -203,6 +204,26 @@ Read-Only:
- `to_clean` (Boolean)
- `updated_time` (Number)
+
+### Nested Schema for `disks.iotune`
+
+Optional:
+
+- `read_bytes_sec` (Number)
+- `read_bytes_sec_max` (Number)
+- `read_iops_sec` (Number)
+- `read_iops_sec_max` (Number)
+- `size_iops_sec` (Number)
+- `total_bytes_sec` (Number)
+- `total_bytes_sec_max` (Number)
+- `total_iops_sec` (Number)
+- `total_iops_sec_max` (Number)
+- `write_bytes_sec` (Number)
+- `write_bytes_sec_max` (Number)
+- `write_iops_sec` (Number)
+- `write_iops_sec_max` (Number)
+
+
### Nested Schema for `network`
@@ -311,6 +332,7 @@ Read-Only:
- `disk_name` (String)
- `disk_type` (String)
- `image_id` (Number)
+- `iotune` (List of Object) (see [below for nested schema](#nestedobjatt--boot_disk--iotune))
- `permanently` (Boolean)
- `pool` (String)
- `present_to` (Map of Number)
@@ -323,6 +345,26 @@ Read-Only:
- `to_clean` (Boolean)
- `updated_time` (Number)
+
+### Nested Schema for `boot_disk.iotune`
+
+Read-Only:
+
+- `read_bytes_sec` (Number)
+- `read_bytes_sec_max` (Number)
+- `read_iops_sec` (Number)
+- `read_iops_sec_max` (Number)
+- `size_iops_sec` (Number)
+- `total_bytes_sec` (Number)
+- `total_bytes_sec_max` (Number)
+- `total_iops_sec` (Number)
+- `total_iops_sec_max` (Number)
+- `write_bytes_sec` (Number)
+- `write_bytes_sec_max` (Number)
+- `write_iops_sec` (Number)
+- `write_iops_sec_max` (Number)
+
+
### Nested Schema for `interfaces`
diff --git a/go.mod b/go.mod
index f03ca8fc..9ad304eb 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,7 @@ require (
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1
github.com/sirupsen/logrus v1.9.0
golang.org/x/net v0.44.0
- repository.basistech.ru/BASIS/decort-golang-sdk v1.12.10
+ repository.basistech.ru/BASIS/decort-golang-sdk v1.12.11
)
require (
diff --git a/go.sum b/go.sum
index c9956042..69942e2e 100644
--- a/go.sum
+++ b/go.sum
@@ -318,5 +318,5 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-repository.basistech.ru/BASIS/decort-golang-sdk v1.12.10 h1:7RLk2Vjl6evKo4dLxlxiQCrRJSSlUwLztO3ZE/uBt+8=
-repository.basistech.ru/BASIS/decort-golang-sdk v1.12.10/go.mod h1:S/f7GxwWcE88eFpORV+I9xqEf8zDW5srQHpG2XQCLZM=
+repository.basistech.ru/BASIS/decort-golang-sdk v1.12.11 h1:Y5meH2uCSsH7QOsQqqyRxSObRIGtmXB9pKbJZDCKzsw=
+repository.basistech.ru/BASIS/decort-golang-sdk v1.12.11/go.mod h1:S/f7GxwWcE88eFpORV+I9xqEf8zDW5srQHpG2XQCLZM=
diff --git a/internal/service/cloudapi/kvmvm/flattens.go b/internal/service/cloudapi/kvmvm/flattens.go
index cdb955bf..0ae41db3 100644
--- a/internal/service/cloudapi/kvmvm/flattens.go
+++ b/internal/service/cloudapi/kvmvm/flattens.go
@@ -333,6 +333,7 @@ func flattenComputeDisksDemo(disksList compute.ListComputeDisks, disksBlocks, ex
"deleted_time": disk.DeletedTime,
"updated_time": disk.UpdatedTime,
"permanently": pernamentlyValue,
+ "iotune": flattenIotune(disk.IOTune),
}
res = append(res, temp)
indexDataDisks++
diff --git a/internal/service/cloudapi/kvmvm/resource_compute.go b/internal/service/cloudapi/kvmvm/resource_compute.go
index 46ea0f8d..cdca311c 100644
--- a/internal/service/cloudapi/kvmvm/resource_compute.go
+++ b/internal/service/cloudapi/kvmvm/resource_compute.go
@@ -384,6 +384,12 @@ func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interf
}
}
+ if _, ok := d.GetOk("disks"); ok {
+ if err := utilityComputeCreateIOTune(ctx, d, m); err != nil {
+ warnings.Add(err)
+ }
+ }
+
if !cleanup {
if enabled, ok := d.GetOk("enabled"); ok {
@@ -1118,6 +1124,7 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf
resizedDisks := make([]interface{}, 0)
renamedDisks := make([]interface{}, 0)
changeStoragePolicyDisks := make([]interface{}, 0)
+ iotuneUpdatedDisks := make([]interface{}, 0)
oldDisks, newDisks := d.GetChange("disks")
oldConv := oldDisks.([]interface{})
@@ -1158,6 +1165,9 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf
if isChangeStoragePolicy(oldConv, el) {
changeStoragePolicyDisks = append(changeStoragePolicyDisks, el)
}
+ if isChangeIOTuneDisk(oldConv, el) {
+ iotuneUpdatedDisks = append(iotuneUpdatedDisks, el)
+ }
}
if len(deletedDisks) > 0 {
@@ -1210,10 +1220,33 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf
if diskConv["image_id"].(int) != 0 {
req.ImageID = uint64(diskConv["image_id"].(int))
}
- _, err := c.CloudAPI().Compute().DiskAdd(ctx, req)
+ diskID, err := c.CloudAPI().Compute().DiskAdd(ctx, req)
if err != nil {
return diag.FromErr(err)
}
+ if iotuneRaw, ok := diskConv["iotune"].([]interface{}); ok && len(iotuneRaw) > 0 {
+ iotuneMap := iotuneRaw[0].(map[string]interface{})
+ limitReq := disks.LimitIORequest{
+ DiskID: diskID,
+ ReadBytesSec: uint64(iotuneMap["read_bytes_sec"].(int)),
+ ReadBytesSecMax: uint64(iotuneMap["read_bytes_sec_max"].(int)),
+ ReadIOPSSec: uint64(iotuneMap["read_iops_sec"].(int)),
+ ReadIOPSSecMax: uint64(iotuneMap["read_iops_sec_max"].(int)),
+ SizeIOPSSec: uint64(iotuneMap["size_iops_sec"].(int)),
+ TotalBytesSec: uint64(iotuneMap["total_bytes_sec"].(int)),
+ TotalBytesSecMax: uint64(iotuneMap["total_bytes_sec_max"].(int)),
+ TotalIOPSSec: uint64(iotuneMap["total_iops_sec"].(int)),
+ TotalIOPSSecMax: uint64(iotuneMap["total_iops_sec_max"].(int)),
+ WriteBytesSec: uint64(iotuneMap["write_bytes_sec"].(int)),
+ WriteBytesSecMax: uint64(iotuneMap["write_bytes_sec_max"].(int)),
+ WriteIOPSSec: uint64(iotuneMap["write_iops_sec"].(int)),
+ WriteIOPSSecMax: uint64(iotuneMap["write_iops_sec_max"].(int)),
+ }
+ _, err := c.CloudAPI().Disks().LimitIO(ctx, limitReq)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ }
}
}
@@ -1267,6 +1300,59 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf
}
}
}
+
+ if len(iotuneUpdatedDisks) > 0 {
+ computeRec, err := utilityComputeCheckPresence(ctx, d, m)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ allDisks := computeRec.Disks
+ bootDisk := findBootDisk(allDisks)
+ diskIDs := getComputeDiskIDs(d, allDisks)
+ for i, disk := range iotuneUpdatedDisks {
+ diskConv := disk.(map[string]interface{})
+ if diskConv["disk_type"].(string) == "B" {
+ continue
+ }
+ if bootDisk != nil && diskConv["disk_id"].(int) == int(bootDisk.ID) {
+ continue
+ }
+ var diskID uint64
+ if id := diskConv["disk_id"].(int); id != 0 {
+ diskID = uint64(id)
+ } else if i < len(diskIDs) {
+ diskID = diskIDs[i]
+ }
+ if diskID == 0 {
+ continue
+ }
+ iotuneRaw, ok := diskConv["iotune"].([]interface{})
+ if !ok || len(iotuneRaw) == 0 {
+ continue
+ }
+ iotuneMap := iotuneRaw[0].(map[string]interface{})
+ limitReq := disks.LimitIORequest{
+ DiskID: diskID,
+ ReadBytesSec: uint64(iotuneMap["read_bytes_sec"].(int)),
+ ReadBytesSecMax: uint64(iotuneMap["read_bytes_sec_max"].(int)),
+ ReadIOPSSec: uint64(iotuneMap["read_iops_sec"].(int)),
+ ReadIOPSSecMax: uint64(iotuneMap["read_iops_sec_max"].(int)),
+ SizeIOPSSec: uint64(iotuneMap["size_iops_sec"].(int)),
+ TotalBytesSec: uint64(iotuneMap["total_bytes_sec"].(int)),
+ TotalBytesSecMax: uint64(iotuneMap["total_bytes_sec_max"].(int)),
+ TotalIOPSSec: uint64(iotuneMap["total_iops_sec"].(int)),
+ TotalIOPSSecMax: uint64(iotuneMap["total_iops_sec_max"].(int)),
+ WriteBytesSec: uint64(iotuneMap["write_bytes_sec"].(int)),
+ WriteBytesSecMax: uint64(iotuneMap["write_bytes_sec_max"].(int)),
+ WriteIOPSSec: uint64(iotuneMap["write_iops_sec"].(int)),
+ WriteIOPSSecMax: uint64(iotuneMap["write_iops_sec_max"].(int)),
+ }
+ _, err := c.CloudAPI().Disks().LimitIO(ctx, limitReq)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ }
+ }
}
if d.HasChange("affinity_label") {
@@ -1866,6 +1952,40 @@ func isContainsDisk(els []interface{}, el interface{}) bool {
return false
}
+func isChangeIOTuneDisk(els []interface{}, el interface{}) bool {
+ for _, elOld := range els {
+ elOldConv := elOld.(map[string]interface{})
+ elConv := el.(map[string]interface{})
+ if elOldConv["disk_id"].(int) != elConv["disk_id"].(int) {
+ continue
+ }
+ oldIOTune := elOldConv["iotune"].([]interface{})
+ newIOTune := elConv["iotune"].([]interface{})
+ if len(oldIOTune) == 0 && len(newIOTune) == 0 {
+ return false
+ }
+ if len(oldIOTune) == 0 || len(newIOTune) == 0 {
+ return true
+ }
+ oldMap := oldIOTune[0].(map[string]interface{})
+ newMap := newIOTune[0].(map[string]interface{})
+ return oldMap["read_bytes_sec"].(int) != newMap["read_bytes_sec"].(int) ||
+ oldMap["read_bytes_sec_max"].(int) != newMap["read_bytes_sec_max"].(int) ||
+ oldMap["read_iops_sec"].(int) != newMap["read_iops_sec"].(int) ||
+ oldMap["read_iops_sec_max"].(int) != newMap["read_iops_sec_max"].(int) ||
+ oldMap["size_iops_sec"].(int) != newMap["size_iops_sec"].(int) ||
+ oldMap["total_bytes_sec"].(int) != newMap["total_bytes_sec"].(int) ||
+ oldMap["total_bytes_sec_max"].(int) != newMap["total_bytes_sec_max"].(int) ||
+ oldMap["total_iops_sec"].(int) != newMap["total_iops_sec"].(int) ||
+ oldMap["total_iops_sec_max"].(int) != newMap["total_iops_sec_max"].(int) ||
+ oldMap["write_bytes_sec"].(int) != newMap["write_bytes_sec"].(int) ||
+ oldMap["write_bytes_sec_max"].(int) != newMap["write_bytes_sec_max"].(int) ||
+ oldMap["write_iops_sec"].(int) != newMap["write_iops_sec"].(int) ||
+ oldMap["write_iops_sec_max"].(int) != newMap["write_iops_sec_max"].(int)
+ }
+ return false
+}
+
func isContainsAR(els []interface{}, el interface{}) bool {
for _, elOld := range els {
elOldConv := elOld.(map[string]interface{})
@@ -1960,6 +2080,81 @@ func disksSubresourceSchemaMake() map[string]*schema.Schema {
Optional: true,
Description: "Disk deletion status",
},
+ "iotune": {
+ Type: schema.TypeList,
+ Optional: true,
+ Computed: true,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "read_bytes_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "read_bytes_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "read_iops_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "read_iops_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "size_iops_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "total_bytes_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "total_bytes_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "total_iops_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "total_iops_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "write_bytes_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "write_bytes_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "write_iops_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "write_iops_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ },
"disk_id": {
Type: schema.TypeInt,
Computed: true,
diff --git a/internal/service/cloudapi/kvmvm/utility_compute.go b/internal/service/cloudapi/kvmvm/utility_compute.go
index a0fb6f18..12789c5e 100644
--- a/internal/service/cloudapi/kvmvm/utility_compute.go
+++ b/internal/service/cloudapi/kvmvm/utility_compute.go
@@ -43,6 +43,7 @@ import (
"github.com/hashicorp/go-cty/cty"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
+ "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/disks"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -622,3 +623,94 @@ func enabledNetwork(rawNetworkConfig cty.Value, netID uint64, netType string) bo
return false
}
+
+func getComputeDiskIDs(d *schema.ResourceData, disksList compute.ListComputeDisks) []uint64 {
+ diskList := d.Get("disks").([]interface{})
+ ids := make([]uint64, 0, len(diskList))
+ for _, item := range diskList {
+ diskConv := item.(map[string]interface{})
+ diskName := diskConv["disk_name"].(string)
+ for _, disk := range disksList {
+ if disk.Name == diskName {
+ ids = append(ids, disk.ID)
+ break
+ }
+ }
+ }
+ return ids
+}
+
+func utilityComputeCreateIOTune(ctx context.Context, d *schema.ResourceData, m interface{}) error {
+ diskList, ok := d.GetOk("disks")
+ if !ok {
+ return nil
+ }
+ disksSlice := diskList.([]interface{})
+
+ hasIotune := false
+ for _, item := range disksSlice {
+ diskConv := item.(map[string]interface{})
+ if iotuneRaw, ok := diskConv["iotune"].([]interface{}); ok && len(iotuneRaw) > 0 {
+ hasIotune = true
+ break
+ }
+ }
+ if !hasIotune {
+ return nil
+ }
+
+ computeRec, err := utilityComputeCheckPresence(ctx, d, m)
+ if err != nil {
+ return err
+ }
+ allDisks := computeRec.Disks
+ bootDisk := findBootDisk(allDisks)
+ diskIDs := getComputeDiskIDs(d, allDisks)
+
+ c := m.(*controller.ControllerCfg)
+
+ for i, item := range disksSlice {
+ diskConv := item.(map[string]interface{})
+ if diskConv["disk_type"].(string) == "B" {
+ continue
+ }
+ if bootDisk != nil && diskConv["disk_id"].(int) == int(bootDisk.ID) {
+ continue
+ }
+ iotuneRaw, ok := diskConv["iotune"].([]interface{})
+ if !ok || len(iotuneRaw) == 0 {
+ continue
+ }
+ var diskID uint64
+ if id := diskConv["disk_id"].(int); id != 0 {
+ diskID = uint64(id)
+ } else if i < len(diskIDs) {
+ diskID = diskIDs[i]
+ }
+ if diskID == 0 {
+ continue
+ }
+ iotuneMap := iotuneRaw[0].(map[string]interface{})
+ limitReq := disks.LimitIORequest{
+ DiskID: diskID,
+ ReadBytesSec: uint64(iotuneMap["read_bytes_sec"].(int)),
+ ReadBytesSecMax: uint64(iotuneMap["read_bytes_sec_max"].(int)),
+ ReadIOPSSec: uint64(iotuneMap["read_iops_sec"].(int)),
+ ReadIOPSSecMax: uint64(iotuneMap["read_iops_sec_max"].(int)),
+ SizeIOPSSec: uint64(iotuneMap["size_iops_sec"].(int)),
+ TotalBytesSec: uint64(iotuneMap["total_bytes_sec"].(int)),
+ TotalBytesSecMax: uint64(iotuneMap["total_bytes_sec_max"].(int)),
+ TotalIOPSSec: uint64(iotuneMap["total_iops_sec"].(int)),
+ TotalIOPSSecMax: uint64(iotuneMap["total_iops_sec_max"].(int)),
+ WriteBytesSec: uint64(iotuneMap["write_bytes_sec"].(int)),
+ WriteBytesSecMax: uint64(iotuneMap["write_bytes_sec_max"].(int)),
+ WriteIOPSSec: uint64(iotuneMap["write_iops_sec"].(int)),
+ WriteIOPSSecMax: uint64(iotuneMap["write_iops_sec_max"].(int)),
+ }
+ _, err := c.CloudAPI().Disks().LimitIO(ctx, limitReq)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/internal/service/cloudbroker/kvmvm/flattens.go b/internal/service/cloudbroker/kvmvm/flattens.go
index 0384aba6..262e4e0a 100644
--- a/internal/service/cloudbroker/kvmvm/flattens.go
+++ b/internal/service/cloudbroker/kvmvm/flattens.go
@@ -289,6 +289,7 @@ func flattenComputeDisks(disksList compute.ListDisks, disksBlocks, extraDisks []
"delete_by": disk.DeletedBy,
"delete_time": disk.DeletedTime,
"update_time": disk.UpdatedTime,
+ "iotune": flattenIOTune(disk.IOTune),
}
res = append(res, temp)
indexDataDisks++
diff --git a/internal/service/cloudbroker/kvmvm/resource_compute.go b/internal/service/cloudbroker/kvmvm/resource_compute.go
index 80f5c14a..cad6cf52 100644
--- a/internal/service/cloudbroker/kvmvm/resource_compute.go
+++ b/internal/service/cloudbroker/kvmvm/resource_compute.go
@@ -656,6 +656,9 @@ func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interf
if err != nil {
warnings.Add(err)
}
+ if err := utilityComputeCreateIOTune(ctx, d, m); err != nil {
+ warnings.Add(err)
+ }
}
}
diff --git a/internal/service/cloudbroker/kvmvm/schema.go b/internal/service/cloudbroker/kvmvm/schema.go
index 487846cb..3e341fe8 100644
--- a/internal/service/cloudbroker/kvmvm/schema.go
+++ b/internal/service/cloudbroker/kvmvm/schema.go
@@ -3764,6 +3764,81 @@ func resourceComputeSchemaMake() map[string]*schema.Schema {
Optional: true,
Description: "Disk deletion status",
},
+ "iotune": {
+ Type: schema.TypeList,
+ Optional: true,
+ Computed: true,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "read_bytes_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "read_bytes_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "read_iops_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "read_iops_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "size_iops_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "total_bytes_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "total_bytes_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "total_iops_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "total_iops_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "write_bytes_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "write_bytes_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "write_iops_sec": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ "write_iops_sec_max": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ },
"disk_id": {
Type: schema.TypeInt,
Computed: true,
diff --git a/internal/service/cloudbroker/kvmvm/utility_compute.go b/internal/service/cloudbroker/kvmvm/utility_compute.go
index d4c99998..2633d3a1 100644
--- a/internal/service/cloudbroker/kvmvm/utility_compute.go
+++ b/internal/service/cloudbroker/kvmvm/utility_compute.go
@@ -235,6 +235,7 @@ func utilityComputeUpdateDisks(ctx context.Context, d *schema.ResourceData, m in
resizedDisks := make([]interface{}, 0)
renamedDisks := make([]interface{}, 0)
changeStoragePolicyDisks := make([]interface{}, 0)
+ iotuneUpdatedDisks := make([]interface{}, 0)
presentNewDisks := make([]interface{}, 0)
presentOldDisks := make([]interface{}, 0)
@@ -283,6 +284,9 @@ func utilityComputeUpdateDisks(ctx context.Context, d *schema.ResourceData, m in
if isChangeStoragePolicy(oldConv, el) {
changeStoragePolicyDisks = append(changeStoragePolicyDisks, el)
}
+ if isChangeIOTuneDisk(oldConv, el) {
+ iotuneUpdatedDisks = append(iotuneUpdatedDisks, el)
+ }
}
if len(deletedDisks) > 0 {
@@ -353,9 +357,33 @@ func utilityComputeUpdateDisks(ctx context.Context, d *schema.ResourceData, m in
}
}
}
- if err != nil {
- return err
+ if iotuneRaw, ok := diskConv["iotune"].([]interface{}); ok && len(iotuneRaw) > 0 {
+ if diskConv["disk_type"].(string) == "B" {
+ continue
+ }
+ iotuneMap := iotuneRaw[0].(map[string]interface{})
+ limitReq := disks.LimitIORequest{
+ DiskID: diskID,
+ ReadBytesSec: uint64(iotuneMap["read_bytes_sec"].(int)),
+ ReadBytesSecMax: uint64(iotuneMap["read_bytes_sec_max"].(int)),
+ ReadIOPSSec: uint64(iotuneMap["read_iops_sec"].(int)),
+ ReadIOPSSecMax: uint64(iotuneMap["read_iops_sec_max"].(int)),
+ SizeIOPSSec: uint64(iotuneMap["size_iops_sec"].(int)),
+ TotalBytesSec: uint64(iotuneMap["total_bytes_sec"].(int)),
+ TotalBytesSecMax: uint64(iotuneMap["total_bytes_sec_max"].(int)),
+ TotalIOPSSec: uint64(iotuneMap["total_iops_sec"].(int)),
+ TotalIOPSSecMax: uint64(iotuneMap["total_iops_sec_max"].(int)),
+ WriteBytesSec: uint64(iotuneMap["write_bytes_sec"].(int)),
+ WriteBytesSecMax: uint64(iotuneMap["write_bytes_sec_max"].(int)),
+ WriteIOPSSec: uint64(iotuneMap["write_iops_sec"].(int)),
+ WriteIOPSSecMax: uint64(iotuneMap["write_iops_sec_max"].(int)),
+ }
+ _, err := c.CloudBroker().Disks().LimitIO(ctx, limitReq)
+ if err != nil {
+ return err
+ }
}
+
}
}
@@ -409,6 +437,44 @@ func utilityComputeUpdateDisks(ctx context.Context, d *schema.ResourceData, m in
}
}
+ if len(iotuneUpdatedDisks) > 0 {
+ for _, disk := range iotuneUpdatedDisks {
+ diskConv := disk.(map[string]interface{})
+ if diskConv["disk_type"].(string) == "B" {
+ continue
+ }
+ diskID := uint64(diskConv["disk_id"].(int))
+ if diskID == 0 {
+ continue
+ }
+ iotuneRaw, ok := diskConv["iotune"].([]interface{})
+ if !ok || len(iotuneRaw) == 0 {
+ continue
+ }
+ iotuneMap := iotuneRaw[0].(map[string]interface{})
+ req := disks.LimitIORequest{
+ DiskID: diskID,
+ ReadBytesSec: uint64(iotuneMap["read_bytes_sec"].(int)),
+ ReadBytesSecMax: uint64(iotuneMap["read_bytes_sec_max"].(int)),
+ ReadIOPSSec: uint64(iotuneMap["read_iops_sec"].(int)),
+ ReadIOPSSecMax: uint64(iotuneMap["read_iops_sec_max"].(int)),
+ SizeIOPSSec: uint64(iotuneMap["size_iops_sec"].(int)),
+ TotalBytesSec: uint64(iotuneMap["total_bytes_sec"].(int)),
+ TotalBytesSecMax: uint64(iotuneMap["total_bytes_sec_max"].(int)),
+ TotalIOPSSec: uint64(iotuneMap["total_iops_sec"].(int)),
+ TotalIOPSSecMax: uint64(iotuneMap["total_iops_sec_max"].(int)),
+ WriteBytesSec: uint64(iotuneMap["write_bytes_sec"].(int)),
+ WriteBytesSecMax: uint64(iotuneMap["write_bytes_sec_max"].(int)),
+ WriteIOPSSec: uint64(iotuneMap["write_iops_sec"].(int)),
+ WriteIOPSSecMax: uint64(iotuneMap["write_iops_sec_max"].(int)),
+ }
+ _, err := c.CloudBroker().Disks().LimitIO(ctx, req)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
for i := range presentNewDisks {
newDisk := presentNewDisks[i].(map[string]interface{})
oldDisk := presentOldDisks[i].(map[string]interface{})
@@ -1928,6 +1994,100 @@ func isContainsDisk(els []interface{}, el interface{}) bool {
return false
}
+func isChangeIOTuneDisk(els []interface{}, el interface{}) bool {
+ for _, elOld := range els {
+ elOldConv := elOld.(map[string]interface{})
+ elConv := el.(map[string]interface{})
+ if elOldConv["disk_id"].(int) != elConv["disk_id"].(int) {
+ continue
+ }
+ oldIOTune := elOldConv["iotune"].([]interface{})
+ newIOTune := elConv["iotune"].([]interface{})
+ if len(oldIOTune) == 0 && len(newIOTune) == 0 {
+ return false
+ }
+ if len(oldIOTune) == 0 || len(newIOTune) == 0 {
+ return true
+ }
+ oldMap := oldIOTune[0].(map[string]interface{})
+ newMap := newIOTune[0].(map[string]interface{})
+ return oldMap["read_bytes_sec"].(int) != newMap["read_bytes_sec"].(int) ||
+ oldMap["read_bytes_sec_max"].(int) != newMap["read_bytes_sec_max"].(int) ||
+ oldMap["read_iops_sec"].(int) != newMap["read_iops_sec"].(int) ||
+ oldMap["read_iops_sec_max"].(int) != newMap["read_iops_sec_max"].(int) ||
+ oldMap["size_iops_sec"].(int) != newMap["size_iops_sec"].(int) ||
+ oldMap["total_bytes_sec"].(int) != newMap["total_bytes_sec"].(int) ||
+ oldMap["total_bytes_sec_max"].(int) != newMap["total_bytes_sec_max"].(int) ||
+ oldMap["total_iops_sec"].(int) != newMap["total_iops_sec"].(int) ||
+ oldMap["total_iops_sec_max"].(int) != newMap["total_iops_sec_max"].(int) ||
+ oldMap["write_bytes_sec"].(int) != newMap["write_bytes_sec"].(int) ||
+ oldMap["write_bytes_sec_max"].(int) != newMap["write_bytes_sec_max"].(int) ||
+ oldMap["write_iops_sec"].(int) != newMap["write_iops_sec"].(int) ||
+ oldMap["write_iops_sec_max"].(int) != newMap["write_iops_sec_max"].(int)
+ }
+ return false
+}
+
+func utilityComputeCreateIOTune(ctx context.Context, d *schema.ResourceData, m interface{}) error {
+ c := m.(*controller.ControllerCfg)
+ diskList := d.Get("disks").([]interface{})
+
+ iotuneArr := make([]interface{}, 0, len(diskList))
+ hasAny := false
+ for _, elem := range diskList {
+ diskVal := elem.(map[string]interface{})
+ iotune := diskVal["iotune"].([]interface{})
+ iotuneArr = append(iotuneArr, iotune)
+ if len(iotune) > 0 {
+ hasAny = true
+ }
+ }
+
+ if !hasAny {
+ return nil
+ }
+
+ computeRec, err := utilityComputeCheckPresence(ctx, d, m)
+ if err != nil {
+ return err
+ }
+ bootDisk := findBootDisk(computeRec.Disks)
+ computeDisksIDs := getComputeDiskIDs(computeRec.Disks, diskList, d.Get("extra_disks").(*schema.Set).List(), bootDisk.ID)
+
+ for i, diskID := range computeDisksIDs {
+ if i >= len(iotuneArr) {
+ continue
+ }
+ iotune, ok := iotuneArr[i].([]interface{})
+ if !ok || len(iotune) == 0 {
+ continue
+ }
+ iotuneMap := iotune[0].(map[string]interface{})
+ req := disks.LimitIORequest{
+ DiskID: diskID.(uint64),
+ ReadBytesSec: uint64(iotuneMap["read_bytes_sec"].(int)),
+ ReadBytesSecMax: uint64(iotuneMap["read_bytes_sec_max"].(int)),
+ ReadIOPSSec: uint64(iotuneMap["read_iops_sec"].(int)),
+ ReadIOPSSecMax: uint64(iotuneMap["read_iops_sec_max"].(int)),
+ SizeIOPSSec: uint64(iotuneMap["size_iops_sec"].(int)),
+ TotalBytesSec: uint64(iotuneMap["total_bytes_sec"].(int)),
+ TotalBytesSecMax: uint64(iotuneMap["total_bytes_sec_max"].(int)),
+ TotalIOPSSec: uint64(iotuneMap["total_iops_sec"].(int)),
+ TotalIOPSSecMax: uint64(iotuneMap["total_iops_sec_max"].(int)),
+ WriteBytesSec: uint64(iotuneMap["write_bytes_sec"].(int)),
+ WriteBytesSecMax: uint64(iotuneMap["write_bytes_sec_max"].(int)),
+ WriteIOPSSec: uint64(iotuneMap["write_iops_sec"].(int)),
+ WriteIOPSSecMax: uint64(iotuneMap["write_iops_sec_max"].(int)),
+ }
+ _, err := c.CloudBroker().Disks().LimitIO(ctx, req)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// isChangeNodesDisk get slice of new disks values and current value disk,
// if need change nodes on disk returns true and new disk value, else return false and nil
func isChangeNodesDisk(els []interface{}, elOld interface{}) (bool, interface{}) {
diff --git a/samples/cloudapi/kvmvm/resource_kvmvm/main.tf b/samples/cloudapi/kvmvm/resource_kvmvm/main.tf
index 454ac4b0..b8152064 100644
--- a/samples/cloudapi/kvmvm/resource_kvmvm/main.tf
+++ b/samples/cloudapi/kvmvm/resource_kvmvm/main.tf
@@ -189,6 +189,25 @@ resource "decort_kvmvm" "comp" {
#опциональный параметр
#тип - булев
#permanently = false
+
+ #ограничения ввода-вывода для диска
+ #опциональный параметр
+ #тип - блок
+ #iotune {
+ #read_bytes_sec = 0
+ #read_bytes_sec_max = 0
+ #read_iops_sec = 0
+ #read_iops_sec_max = 0
+ #size_iops_sec = 0
+ #total_bytes_sec = 0
+ #total_bytes_sec_max = 0
+ #total_iops_sec = 0
+ #total_iops_sec_max = 0
+ #write_bytes_sec = 0
+ #write_bytes_sec_max = 0
+ #write_iops_sec = 0
+ #write_iops_sec_max = 0
+ #}
#}
#правила affinity
diff --git a/samples/cloudbroker/kvmvm/resource_kvmvm/main.tf b/samples/cloudbroker/kvmvm/resource_kvmvm/main.tf
index 59c3b3fb..23065e9e 100644
--- a/samples/cloudbroker/kvmvm/resource_kvmvm/main.tf
+++ b/samples/cloudbroker/kvmvm/resource_kvmvm/main.tf
@@ -198,6 +198,25 @@ resource "decort_cb_kvmvm" "comp" {
#опциональный параметр
#тип - булев
#permanently = false
+
+ #ограничения ввода-вывода для диска
+ #опциональный параметр
+ #тип - блок
+ #iotune {
+ #read_bytes_sec = 0
+ #read_bytes_sec_max = 0
+ #read_iops_sec = 0
+ #read_iops_sec_max = 0
+ #size_iops_sec = 0
+ #total_bytes_sec = 0
+ #total_bytes_sec_max = 0
+ #total_iops_sec = 0
+ #total_iops_sec_max = 0
+ #write_bytes_sec = 0
+ #write_bytes_sec_max = 0
+ #write_iops_sec = 0
+ #write_iops_sec_max = 0
+ #}
#}
#правила affinity