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