From bd95fa8b78528923d571c57bb865b30d7b0185e5 Mon Sep 17 00:00:00 2001 From: Alexey Fetisov Date: Thu, 16 Apr 2026 16:23:13 +0300 Subject: [PATCH] 4.9.10 --- CHANGELOG.md | 8 +- Makefile | 2 +- docs/index.md | 2 +- docs/resources/cb_kvmvm.md | 21 +++ docs/resources/kvmvm.md | 42 +++++ internal/service/cloudapi/kvmvm/flattens.go | 1 + .../cloudapi/kvmvm/resource_compute.go | 150 +++++++++++++++- .../service/cloudapi/kvmvm/utility_compute.go | 116 +++++++++++++ .../service/cloudbroker/kvmvm/flattens.go | 1 + .../cloudbroker/kvmvm/resource_compute.go | 3 + internal/service/cloudbroker/kvmvm/schema.go | 75 ++++++++ .../cloudbroker/kvmvm/utility_compute.go | 161 ++++++++++++++++++ samples/cloudapi/kvmvm/resource_kvmvm/main.tf | 21 +++ .../cloudbroker/kvmvm/resource_kvmvm/main.tf | 21 +++ 14 files changed, 617 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b4f4c1..a47f6267 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -## Version 4.9.9 +## Version 4.9.10 -### Исправлено +### Добавлено -#### kvmvmm +#### kvmvm | Идентификатор
задачи | Описание | | --- | --- | -| BATF-1241 | Ошибки применения новой конфигурации после импортирования в resource `decort_kvmvm` в cloudapi/kvmvm и в resource `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 e4541289..f05a3363 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.9.9 +VERSION=4.9.10 OS_ARCH=$(shell go env GOHOSTOS)_$(shell go env GOHOSTARCH) FILES = ${BINARY}_${VERSION}_darwin_amd64\ diff --git a/docs/index.md b/docs/index.md index 4865f24e..d324b72b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,6 +18,7 @@ description: |- ### Required - `authenticator` (String) Authentication mode to use when connecting to DECORT cloud API. Should be one of 'decs3o', 'legacy', 'jwt' or 'bvs'. +- `controller_url` (String) URL of DECORT Cloud controller to use. API calls will be directed to this URL. ### Optional @@ -26,7 +27,6 @@ description: |- - `app_secret` (String) Application secret to access DECORT cloud API in 'decs3o' and 'bvs' authentication mode. - `bvs_password` (String) User password for DECORT cloud API operations in 'bvs' authentication mode. - `bvs_user` (String) User name for DECORT cloud API operations in 'bvs' authentication mode. -- `controller_url` (String) URL of DECORT Cloud controller to use. API calls will be directed to this URL. - `domain` (String) User password for DECORT cloud API operations in 'bvs' authentication mode. - `jwt` (String) JWT to access DECORT cloud API in 'jwt' authentication mode. - `oauth2_url` (String) OAuth2 application URL in 'decs3o' and 'bvs' authentication mode. diff --git a/docs/resources/cb_kvmvm.md b/docs/resources/cb_kvmvm.md index dab7495d..cbccc3e5 100644 --- a/docs/resources/cb_kvmvm.md +++ b/docs/resources/cb_kvmvm.md @@ -183,6 +183,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 @@ -196,6 +197,26 @@ Read-Only: - `size_max` (Number) - `size_used` (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 874051be..fe55638d 100644 --- a/docs/resources/kvmvm.md +++ b/docs/resources/kvmvm.md @@ -177,6 +177,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 @@ -189,6 +190,26 @@ Read-Only: - `size_max` (Number) - `size_used` (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` @@ -276,6 +297,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) @@ -285,6 +307,26 @@ Read-Only: - `size_max` (Number) - `size_used` (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/internal/service/cloudapi/kvmvm/flattens.go b/internal/service/cloudapi/kvmvm/flattens.go index 04942d88..a6b100b1 100644 --- a/internal/service/cloudapi/kvmvm/flattens.go +++ b/internal/service/cloudapi/kvmvm/flattens.go @@ -307,6 +307,7 @@ func flattenComputeDisksDemo(disksList compute.ListComputeDisks, disksBlocks, ex "size": disk.SizeMax, "permanently": permanentlyValue, "present_to": disk.PresentTo, + "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 2e827716..c8475275 100644 --- a/internal/service/cloudapi/kvmvm/resource_compute.go +++ b/internal/service/cloudapi/kvmvm/resource_compute.go @@ -587,6 +587,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) + } + } + } log.Debugf("resourceComputeCreate: new Compute ID %d, name %s creation sequence complete", computeId, d.Get("name").(string)) @@ -1063,6 +1069,7 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf addedDisks := make([]interface{}, 0) resizedDisks := make([]interface{}, 0) renamedDisks := make([]interface{}, 0) + iotuneUpdatedDisks := make([]interface{}, 0) oldDisks, newDisks := d.GetChange("disks") oldConv := oldDisks.([]interface{}) @@ -1100,6 +1107,9 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf if isRenameDisk(oldConv, el) { renamedDisks = append(renamedDisks, el) } + if isChangeIOTuneDisk(oldConv, el) { + iotuneUpdatedDisks = append(iotuneUpdatedDisks, el) + } } if len(deletedDisks) > 0 { @@ -1153,10 +1163,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) + } + } } } @@ -1194,6 +1227,46 @@ func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interf } } } + + 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 := diskConv["iotune"].([]interface{}) + if 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.CloudAPI().Disks().LimitIO(ctx, req) + if err != nil { + return diag.FromErr(err) + } + } + } } if d.HasChange("affinity_label") { @@ -1818,6 +1891,81 @@ func disksSubresourceSchemaMake() map[string]*schema.Schema { Default: false, 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 acd4c8ab..7155069d 100644 --- a/internal/service/cloudapi/kvmvm/utility_compute.go +++ b/internal/service/cloudapi/kvmvm/utility_compute.go @@ -40,6 +40,7 @@ import ( 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" @@ -433,3 +434,118 @@ func differenceNetwork(oldList, newList []interface{}) (detachMap, changeIpMap, return } + +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.CloudAPI().Disks().LimitIO(ctx, req) + if err != nil { + return err + } + } + + return nil +} + +func getComputeDiskIDs(disksList compute.ListComputeDisks, disksBlocks, extraDisks []interface{}, bootDiskId uint64) []interface{} { + res := make([]interface{}, 0) + + if len(disksBlocks) == 0 { + return res + } + + sort.Slice(disksList, func(i, j int) bool { + return disksList[i].ID < disksList[j].ID + }) + + for _, disk := range disksList { + if disk.ID == bootDiskId || findInExtraDisks(uint(disk.ID), extraDisks) { //skip main bootdisk and extraDisks + continue + } + + res = append(res, disk.ID) + } + + return res +} +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 +} diff --git a/internal/service/cloudbroker/kvmvm/flattens.go b/internal/service/cloudbroker/kvmvm/flattens.go index 7cdee309..2760fea6 100644 --- a/internal/service/cloudbroker/kvmvm/flattens.go +++ b/internal/service/cloudbroker/kvmvm/flattens.go @@ -262,6 +262,7 @@ func flattenComputeDisks(disksList compute.ListDisks, disksBlocks, extraDisks [] "size_max": disk.SizeMax, "permanently": permanentlyValue, "present_to": disk.PresentTo, + "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 66344d23..4e7e6857 100644 --- a/internal/service/cloudbroker/kvmvm/resource_compute.go +++ b/internal/service/cloudbroker/kvmvm/resource_compute.go @@ -585,6 +585,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 22ec4a03..79bb0bba 100644 --- a/internal/service/cloudbroker/kvmvm/schema.go +++ b/internal/service/cloudbroker/kvmvm/schema.go @@ -3499,6 +3499,81 @@ func resourceComputeSchemaMake() map[string]*schema.Schema { Type: schema.TypeInt, Computed: true, }, + "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, + }, + }, + }, + }, }, }, }, diff --git a/internal/service/cloudbroker/kvmvm/utility_compute.go b/internal/service/cloudbroker/kvmvm/utility_compute.go index be7b33e9..2130e743 100644 --- a/internal/service/cloudbroker/kvmvm/utility_compute.go +++ b/internal/service/cloudbroker/kvmvm/utility_compute.go @@ -233,6 +233,7 @@ func utilityComputeUpdateDisks(ctx context.Context, d *schema.ResourceData, m in addedDisks := make([]interface{}, 0) resizedDisks := make([]interface{}, 0) renamedDisks := make([]interface{}, 0) + iotuneUpdatedDisks := make([]interface{}, 0) presentNewDisks := make([]interface{}, 0) presentOldDisks := make([]interface{}, 0) @@ -278,6 +279,9 @@ func utilityComputeUpdateDisks(ctx context.Context, d *schema.ResourceData, m in if isRenameDisk(oldConv, el) { renamedDisks = append(renamedDisks, el) } + if isChangeIOTuneDisk(oldConv, el) { + iotuneUpdatedDisks = append(iotuneUpdatedDisks, el) + } } if len(deletedDisks) > 0 { @@ -349,6 +353,29 @@ func utilityComputeUpdateDisks(ctx context.Context, d *schema.ResourceData, m in } } } + 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.CloudBroker().Disks().LimitIO(ctx, limitReq) + if err != nil { + return err + } + } if err != nil { return err } @@ -390,6 +417,46 @@ 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 := diskConv["iotune"].([]interface{}) + if 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{}) @@ -1638,6 +1705,66 @@ func utilityComputeStart(ctx context.Context, computeID uint64, m interface{}) ( return 0, nil } +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 +} + func utilityComputeCreatePresentDisk(ctx context.Context, d *schema.ResourceData, m interface{}) error { c := m.(*controller.ControllerCfg) var errs error @@ -1683,6 +1810,40 @@ func utilityComputeCreatePresentDisk(ctx context.Context, d *schema.ResourceData return errs } +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 isResizeDisk(els []interface{}, el interface{}) bool { for _, elOld := range els { elOldConv := elOld.(map[string]interface{}) diff --git a/samples/cloudapi/kvmvm/resource_kvmvm/main.tf b/samples/cloudapi/kvmvm/resource_kvmvm/main.tf index cc652cca..d38e5287 100644 --- a/samples/cloudapi/kvmvm/resource_kvmvm/main.tf +++ b/samples/cloudapi/kvmvm/resource_kvmvm/main.tf @@ -185,6 +185,27 @@ 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 = 3000 + #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 9de69474..d104fd0c 100644 --- a/samples/cloudbroker/kvmvm/resource_kvmvm/main.tf +++ b/samples/cloudbroker/kvmvm/resource_kvmvm/main.tf @@ -194,6 +194,27 @@ 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 = 3000 + #total_iops_sec_max = 0 + #write_bytes_sec = 0 + #write_bytes_sec_max = 0 + #write_iops_sec = 0 + #write_iops_sec_max = 0 + #} #} #правила affinity