/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
Tim Tkachev, <tvkachev@basistech.ru>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.

Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort

Please see README.md to learn where to place source code so that it
builds seamlessly.

Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/

package kvmvm

import (
	"context"
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"

	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/decort-golang-sdk/pkg/cloudapi/kvmx86"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/dc"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/statefuncs"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/status"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/validators"

	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceComputeCreate: called for Compute name %q, RG ID %d", d.Get("name").(string), d.Get("rg_id").(int))
	c := m.(*controller.ControllerCfg)
	createReqX86 := kvmx86.CreateRequest{}

	hasRG, err := existRgID(ctx, d, m)
	if err != nil {
		return diag.FromErr(err)
	}

	if !hasRG {
		return diag.Errorf("resourceComputeCreate: can't create Compute because rgID %d is not allowed or does not exist", d.Get("rg_id").(int))
	}

	if !d.Get("create_blank").(bool) {
		hasImage, err := existImageId(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}

		if !hasImage {
			return diag.Errorf("resourceComputeCreate: can't create Compute because imageID %d is not allowed or does not exist", d.Get("image_id").(int))
		}
	}

	if zoneID, ok := d.GetOk("zone_id"); ok {
		createReqX86.ZoneID = uint64(zoneID.(int))
	}

	if network, ok := d.GetOk("network"); ok {
		networkList := network.(*schema.Set).List()
		for _, elem := range networkList {
			networkData := elem.(map[string]interface{})

			switch networkData["net_type"].(string) {
			case "VINS":
				if vinsId, ok := existVinsId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeCreate: can't create compute because vins ID %d is not allowed or does not exist", vinsId)
				}
			case "EXTNET":
				if extNetId, ok := existExtNetId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeCreate: can't create compute because extnet ID %d is not allowed or does not exist", extNetId)
				}
			case "VFNIC":
				if vfpoolId, ok := existVFPoolId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeCreate: can't create compute because vfpool ID %d is not allowed or does not exist", vfpoolId)
				}
			case "DPDK":
				if dpdkId, ok := existDPDKNetId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeCreate: can't create compute because DPDK ID %d is not allowed or does not exist", dpdkId)
				}
			case "TRUNK":
				if trunkId, ok := existTRUNKId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeCreate: can't create compute because TRUNK ID %d is not allowed or does not exist", trunkId)
				}
			default:
				continue
			}
		}
	}

	argVal, ok := d.GetOk("description")
	if ok {
		createReqX86.Description = argVal.(string)
	}

	if sepID, ok := d.GetOk("sep_id"); ok {
		createReqX86.SepID = uint64(sepID.(int))
	}

	if pool, ok := d.GetOk("pool"); ok {
		createReqX86.Pool = pool.(string)
	}

	if bootSize, ok := d.GetOk("boot_disk_size"); ok {
		createReqX86.BootDisk = uint64(bootSize.(int))
	}

	createReqX86.Interfaces = make([]kvmx86.Interface, 0)

	if networks, ok := d.GetOk("network"); ok {
		if networks.(*schema.Set).Len() > 0 {
			ns := networks.(*schema.Set).List()

			sort.Slice(ns, func(i, j int) bool {
				weightI := ns[i].(map[string]interface{})["weight"].(int)
				weightJ := ns[j].(map[string]interface{})["weight"].(int)
				if weightI == 0 {
					return false
				}
				if weightJ == 0 {
					return true
				}
				return weightI < weightJ
			})
			interfaces := make([]kvmx86.Interface, 0)
			for _, elem := range ns {
				netInterfaceVal := elem.(map[string]interface{})
				reqInterface := kvmx86.Interface{
					NetType: netInterfaceVal["net_type"].(string),
					NetID:   uint64(netInterfaceVal["net_id"].(int)),
				}

				if enabledNetwork(d.GetRawConfig().GetAttr("network"), reqInterface.NetID, reqInterface.NetType) {
					reqInterface.Enabled = netInterfaceVal["enabled"].(bool)
				}

				if reqInterface.NetType == "DPDK" || reqInterface.NetType == "EXTNET" || reqInterface.NetType == "TRUNK" {
					reqInterface.MTU = uint64(netInterfaceVal["mtu"].(int))
				}

				if reqInterface.NetType == "DPDK" || reqInterface.NetType == "VFNIC" {
					if netMask, netMaskSet := netInterfaceVal["net_mask"]; netMaskSet {
						reqInterface.NetMask = uint64(netMask.(int))
					}
				}

				ipaddr, ipSet := netInterfaceVal["ip_address"]
				if ipSet {
					reqInterface.IPAddr = ipaddr.(string)
				}

				macaddr, macSet := netInterfaceVal["mac"]
				if macSet {
					reqInterface.MAC = macaddr.(string)
				}

				sdnID, sdnSet := netInterfaceVal["sdn_interface_id"]
				if sdnSet {
					reqInterface.SDNInterfaceID = sdnID.(string)
				}

				interfaces = append(interfaces, reqInterface)
			}

			createReqX86.Interfaces = interfaces
		}
	}

	if disks, ok := d.GetOk("disks"); ok {
		disksX86 := make([]kvmx86.DataDisk, 0)

		for _, elem := range disks.([]interface{}) {
			diskVal := elem.(map[string]interface{})
			reqDataDisk := kvmx86.DataDisk{
				DiskName:        diskVal["disk_name"].(string),
				Size:            uint64(diskVal["size"].(int)),
				StoragePolicyID: uint64(diskVal["storage_policy_id"].(int)),
			}
			if sepId, ok := diskVal["sep_id"]; ok {
				reqDataDisk.SepID = uint64(sepId.(int))
			}
			if pool, ok := diskVal["pool"]; ok {
				reqDataDisk.Pool = pool.(string)
			}
			if desc, ok := diskVal["desc"]; ok {
				reqDataDisk.Description = desc.(string)
			}
			if imageID, ok := diskVal["image_id"]; ok {
				reqDataDisk.ImageID = uint64(imageID.(int))
			}
			disksX86 = append(disksX86, reqDataDisk)
		}

		createReqX86.DataDisks = disksX86

	}

	argVal, ok = d.GetOk("cloud_init")
	if ok {
		userdata := argVal.(string)
		if userdata != "" && userdata != "applied" {
			createReqX86.Userdata = strings.TrimSpace(userdata)
		}
	}

	var computeId uint64

	createReqX86.RGID = uint64(d.Get("rg_id").(int))
	createReqX86.Name = d.Get("name").(string)
	createReqX86.CPU = uint64(d.Get("cpu").(int))
	createReqX86.RAM = uint64(d.Get("ram").(int))
	createReqX86.StoragePolicyID = uint64(d.Get("storage_policy_id").(int))

	if image, ok := d.GetOk("image_id"); ok {
		createReqX86.ImageID = uint64(image.(int))
	}
	if withoutBootDisk, ok := d.GetOk("without_boot_disk"); ok {
		createReqX86.WithoutBootDisk = withoutBootDisk.(bool)
	}

	if custom_fields, ok := d.GetOk("custom_fields"); ok {
		val := custom_fields.(string)
		val = strings.ReplaceAll(val, "\\", "")
		val = strings.ReplaceAll(val, "\n", "")
		val = strings.ReplaceAll(val, "\t", "")
		val = strings.TrimSpace(val)

		createReqX86.CustomFields = val
	}

	if numaAffinity, ok := d.GetOk("numa_affinity"); ok {
		createReqX86.NumaAffinity = numaAffinity.(string)
	}
	createReqX86.CPUPin = d.Get("cpu_pin").(bool)
	createReqX86.HPBacked = d.Get("hp_backed").(bool)
	createReqX86.Chipset = d.Get("chipset").(string)

	if preferredCPU, ok := d.GetOk("preferred_cpu"); ok {
		preferredList := preferredCPU.([]interface{})
		if len(preferredList) > 0 {
			for _, v := range preferredList {
				cpuNum := v.(int)
				createReqX86.PreferredCPU = append(createReqX86.PreferredCPU, int64(cpuNum))
			}
		}
	}

	if osVersion, ok := d.GetOk("os_version"); ok {
		createReqX86.OSVersion = osVersion.(string)
	}

	log.Debugf("resourceComputeCreate: creating Compute of type KVM VM x86")
	var apiResp uint64
	if d.Get("create_blank").(bool) {
		log.Debugf("resourceComputeCreate: using createBlank endpoint")
		createBlankReq := kvmx86.CreateBlankRequest{
			RGID:            createReqX86.RGID,
			Name:            createReqX86.Name,
			CPU:             createReqX86.CPU,
			RAM:             createReqX86.RAM,
			StoragePolicyID: createReqX86.StoragePolicyID,
			WithoutBootDisk: createReqX86.WithoutBootDisk,
			BootDisk:        createReqX86.BootDisk,
			SEPID:           createReqX86.SepID,
			Pool:            createReqX86.Pool,
			DataDisks:       createReqX86.DataDisks,
			Interfaces:      createReqX86.Interfaces,
			Description:     createReqX86.Description,
			Chipset:         createReqX86.Chipset,
			PreferredCPU:    createReqX86.PreferredCPU,
			ZoneID:          createReqX86.ZoneID,
			OSVersion:       createReqX86.OSVersion,
		}
		apiResp, err = c.CloudAPI().KVMX86().CreateBlank(ctx, createBlankReq)
	} else {
		apiResp, err = c.CloudAPI().KVMX86().Create(ctx, createReqX86)
	}
	if err != nil {
		return diag.FromErr(err)
	}

	d.SetId(strconv.FormatUint(apiResp, 10))
	computeId = apiResp

	warnings := dc.Warnings{}

	simpleCompRec, err := utilityComputeCheckPresence(ctx, d, m)
	if err != nil {
		warnings.Add(err)
	}

	cleanup := false
	defer func() {
		if cleanup {
			req := compute.DeleteRequest{
				ComputeID:   computeId,
				Permanently: true,
				DetachDisks: true,
			}

			if _, err := c.CloudAPI().Compute().Delete(ctx, req); err != nil {
				log.Errorf("resourceComputeCreate: could not delete compute after failed creation: %v", err)
			}

			d.SetId("")
		}
	}()

	log.Debugf("resourceComputeCreate: new simple Compute ID %d, name %s created", computeId, d.Get("name").(string))

	updateReq := compute.UpdateRequest{}

	loaderType, loaderTypeOk := d.GetOk("loader_type")
	bootType, bootTypeOk := d.GetOk("boot_type")
	hotResize, hotResizeOk := d.GetOkExists("hot_resize")
	networkInterfaceNaming, networkInterfaceNamingOk := d.GetOk("network_interface_naming")

	if loaderTypeOk {
		updateReq.LoaderType = loaderType.(string)
	}

	if bootTypeOk {
		updateReq.BootType = bootType.(string)
	}

	if hotResizeOk {
		updateReq.HotResize = hotResize.(bool)
	}

	if networkInterfaceNamingOk {
		updateReq.NetworkInterfaceNaming = networkInterfaceNaming.(string)
	}

	if loaderTypeOk || bootTypeOk || hotResizeOk || networkInterfaceNamingOk {
		log.Debugf("resourceComputeCreate: change loaderType or bootType or hotResize or networkInterfaceNaming on ComputeID: %d", computeId)
		updateReq.ComputeID = computeId
		_, err := c.CloudAPI().Compute().Update(ctx, updateReq)
		if err != nil {
			warnings.Add(err)
		}
	}

	if ars, ok := d.GetOk("pci_devices"); ok {
		log.Debugf("resourceComputeCreate: add pci devices on ComputeID: %d", computeId)
		addedPciDevices := ars.(*schema.Set).List()
		if len(addedPciDevices) > 0 {
			for _, v := range addedPciDevices {
				devicesConv := v.(int)
				req := compute.AttachPCIDeviceRequest{
					ComputeID: computeId,
					DeviceID:  uint64(devicesConv),
				}

				_, err := c.CloudAPI().Compute().AttachPCIDevice(ctx, req)
				if err != nil {
					warnings.Add(err)
				}
			}
		}
	}

	argVal, ok = d.GetOk("extra_disks")
	if ok && argVal.(*schema.Set).Len() > 0 {
		log.Debugf("resourceComputeCreate: calling utilityComputeExtraDisksConfigure to attach %d extra disk(s)", argVal.(*schema.Set).Len())
		err = utilityComputeExtraDisksConfigure(ctx, d, m, false, nil)
		if err != nil {
			log.Errorf("resourceComputeCreate: error when attaching extra disk(s) to a new Compute ID %d: %v", computeId, err)
			cleanup = true
			return diag.FromErr(err)
		}
	}

	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 {
			if enabled.(bool) {
				req := compute.EnableRequest{ComputeID: computeId}
				log.Debugf("resourceComputeCreate: enable=%t Compute ID %d after completing its resource configuration", enabled, computeId)
				if _, err := c.CloudAPI().Compute().Enable(ctx, req); err != nil {
					warnings.Add(err)
				}
			} else {
				req := compute.DisableRequest{ComputeID: computeId}
				log.Debugf("resourceComputeCreate: enable=%t Compute ID %d after completing its resource configuration", enabled, computeId)
				if _, err := c.CloudAPI().Compute().Disable(ctx, req); err != nil {
					warnings.Add(err)
				}
			}
		}

		if secGroups, ok := d.GetOk("security_groups"); ok {
			if secGroups.(*schema.Set).Len() > 0 {
				sgl := secGroups.(*schema.Set).List()
				for _, elem := range sgl {
					secGroupsMap := elem.(map[string]interface{})

					netType := secGroupsMap["net_type"].(string)
					netId := uint64(secGroupsMap["net_id"].(int))
					var mac string
					for _, iface := range simpleCompRec.Interfaces {
						if iface.NetID == netId && iface.NetType == netType {
							mac = iface.MAC
							break
						}
					}
					if mac == "" {
						warnings.Add(fmt.Errorf("add security groups: Network with type %s and id %d is not connected to the compute %d", netType, netId, computeId))
						continue
					}
					secGroupsIDs := make([]uint64, 0)
					for _, id := range secGroupsMap["security_groups"].(*schema.Set).List() {
						secGroupsIDs = append(secGroupsIDs, uint64(id.(int)))
					}
					log.Debugf("resourceComputeCreate: Configure security groups interface parameters on Network with type %s and id %d", netType, netId)
					req := compute.ChangeSecGroupsRequest{
						ComputeID:       computeId,
						Interface:       mac,
						SecGroups:       secGroupsIDs,
						EnableSecGroups: secGroupsMap["enable_secgroups"].(bool),
					}

					_, err := c.CloudAPI().Compute().ChangeSecGroups(ctx, req)
					if err != nil {
						warnings.Add(err)
					}
				}
			}
		}

		// Note bene: we created compute in a STOPPED state (this is required to properly attach 1st network interface),
		// now we need to start it before we report the sequence complete
		if start, ok := d.GetOk("started"); ok {
			if start.(bool) {
				req := compute.StartRequest{ComputeID: computeId}
				if altBootID, ok := d.Get("alt_boot_id").(int); ok {
					req.AltBootID = uint64(altBootID)
				}
				log.Debugf("resourceComputeCreate: starting Compute ID %d after completing its resource configuration", computeId)
				if _, err := c.CloudAPI().Compute().Start(ctx, req); err != nil {
					warnings.Add(err)
				}
			}
			if !start.(bool) {
				req := compute.StopRequest{ComputeID: computeId}
				log.Debugf("resourceComputeCreate: stoping Compute ID %d after completing its resource configuration", computeId)
				if _, err := c.CloudAPI().Compute().Stop(ctx, req); err != nil {
					warnings.Add(err)
				}
			}
		}

		if d.Get("pin_to_node").(bool) {
			if !d.Get("started").(bool) {
				warnings.Add(errors.New("cannot pin to node a VM, VM should be started"))
			}
			if d.Get("started").(bool) {
				req := compute.PinToNodeRequest{
					ComputeID: computeId,
				}
				req.AutoStart = d.Get("auto_start_w_node").(bool)
				_, err = c.CloudAPI().Compute().PinToNode(ctx, req)
				if err != nil {
					warnings.Add(err)
				}
			}
		}

		if affinityLabel, ok := d.GetOk("affinity_label"); ok {
			req := compute.AffinityLabelSetRequest{
				ComputeID:     computeId,
				AffinityLabel: affinityLabel.(string),
			}

			_, err := c.CloudAPI().Compute().AffinityLabelSet(ctx, req)
			if err != nil {
				warnings.Add(err)
			}
		}

		if ars, ok := d.GetOk("affinity_rules"); ok {
			log.Debugf("resourceComputeCreate: Create affinity rules on ComputeID: %d", computeId)
			addedAR := ars.([]interface{})
			if len(addedAR) > 0 {
				for _, ar := range addedAR {
					arConv := ar.(map[string]interface{})
					req := compute.AffinityRuleAddRequest{
						ComputeID: computeId,
						Topology:  arConv["topology"].(string),
						Policy:    arConv["policy"].(string),
						Mode:      arConv["mode"].(string),
						Key:       arConv["key"].(string),
						Value:     arConv["value"].(string),
					}

					_, err := c.CloudAPI().Compute().AffinityRuleAdd(ctx, req)
					if err != nil {
						warnings.Add(err)
					}
				}
			}
		}

		if ars, ok := d.GetOk("anti_affinity_rules"); ok {
			log.Debugf("resourceComputeCreate: Create anti affinity rules on ComputeID: %d", computeId)
			addedAR := ars.([]interface{})
			if len(addedAR) > 0 {
				for _, ar := range addedAR {
					arConv := ar.(map[string]interface{})
					req := compute.AntiAffinityRuleAddRequest{
						ComputeID: computeId,
						Topology:  arConv["topology"].(string),
						Policy:    arConv["policy"].(string),
						Mode:      arConv["mode"].(string),
						Key:       arConv["key"].(string),
						Value:     arConv["value"].(string),
					}

					_, err := c.CloudAPI().Compute().AntiAffinityRuleAdd(ctx, req)
					if err != nil {
						warnings.Add(err)
					}
				}
			}
		}

		if tags, ok := d.GetOk("tags"); ok {
			log.Debugf("resourceComputeCreate: Create tags on ComputeID: %d", computeId)
			addedTags := tags.(*schema.Set).List()
			if len(addedTags) > 0 {
				for _, tagInterface := range addedTags {
					tagItem := tagInterface.(map[string]interface{})
					req := compute.TagAddRequest{
						ComputeID: computeId,
						Key:       tagItem["key"].(string),
						Value:     tagItem["value"].(string),
					}

					_, err := c.CloudAPI().Compute().TagAdd(ctx, req)
					if err != nil {
						warnings.Add(err)
					}
				}
			}
		}

		if pfws, ok := d.GetOk("port_forwarding"); ok {
			log.Debugf("resourceComputeCreate: Create port farwarding on ComputeID: %d", computeId)
			addedPfws := pfws.(*schema.Set).List()
			if len(addedPfws) > 0 {
				for _, pfwInterface := range addedPfws {
					pfwItem := pfwInterface.(map[string]interface{})
					req := compute.PFWAddRequest{
						ComputeID:       computeId,
						PublicPortStart: uint64(pfwItem["public_port_start"].(int)),
						Proto:           pfwItem["proto"].(string),
					}

					if pfwItem["local_port"].(int) != 0 {
						req.LocalBasePort = uint64(pfwItem["local_port"].(int))
					}
					if pfwItem["public_port_end"].(int) != 0 {
						req.PublicPortEnd = int64(pfwItem["public_port_end"].(int))
					}

					_, err := c.CloudAPI().Compute().PFWAdd(ctx, req)
					if err != nil {
						warnings.Add(err)
					}
				}
			}
		}

		if userAcess, ok := d.GetOk("user_access"); ok {
			log.Debugf("resourceComputeCreate: Create user access on ComputeID: %d", computeId)
			usersAcess := userAcess.(*schema.Set).List()
			if len(usersAcess) > 0 {
				for _, userAcessInterface := range usersAcess {
					userAccessItem := userAcessInterface.(map[string]interface{})
					req := compute.UserGrantRequest{
						ComputeID:  computeId,
						Username:   userAccessItem["username"].(string),
						AccessType: userAccessItem["access_type"].(string),
					}

					_, err := c.CloudAPI().Compute().UserGrant(ctx, req)
					if err != nil {
						warnings.Add(err)
					}
				}
			}
		}

		if snapshotList, ok := d.GetOk("snapshot"); ok {
			log.Debugf("resourceComputeCreate: Create snapshot on ComputeID: %d", computeId)
			snapshots := snapshotList.(*schema.Set).List()
			if len(snapshots) > 0 {
				for _, snapshotInterface := range snapshots {
					snapshotItem := snapshotInterface.(map[string]interface{})
					req := compute.SnapshotCreateRequest{
						ComputeID: computeId,
						Label:     snapshotItem["label"].(string),
					}

					_, err := c.CloudAPI().Compute().SnapshotCreate(ctx, req)
					if err != nil {
						warnings.Add(err)
					}
				}
			}
		}

		if cdtList, ok := d.GetOk("cd"); ok {
			log.Debugf("resourceComputeCreate: Create cd on ComputeID: %d", computeId)
			cds := cdtList.(*schema.Set).List()
			if len(cds) > 0 {
				snapshotItem := cds[0].(map[string]interface{})
				req := compute.CDInsertRequest{
					ComputeID: computeId,
					CDROMID:   uint64(snapshotItem["cdrom_id"].(int)),
				}

				_, err := c.CloudAPI().Compute().CDInsert(ctx, req)
				if err != nil {
					warnings.Add(err)
				}
			}
		}

		if !d.Get("pin_to_node").(bool) && d.Get("auto_start_w_node").(bool) {
			req := compute.UpdateRequest{
				ComputeID: computeId,
				AutoStart: d.Get("auto_start_w_node").(bool),
				CPUPin:    d.Get("cpu_pin").(bool),
				HPBacked:  d.Get("hp_backed").(bool),
			}
			_, err := c.CloudAPI().Compute().Update(ctx, req)
			if err != nil {
				warnings.Add(err)
			}
		}

		if d.Get("pause").(bool) {
			req := compute.PauseRequest{
				ComputeID: computeId,
			}
			_, err := c.CloudAPI().Compute().Pause(ctx, req)
			if err != nil {
				warnings.Add(err)
			}
		}

	}

	log.Debugf("resourceComputeCreate: new Compute ID %d, name %s creation sequence complete", computeId, d.Get("name").(string))

	// We may reuse dataSourceComputeRead here as we maintain similarity
	// between Compute resource and Compute data source schemas
	// Compute read function will also update resource ID on success, so that Terraform
	// will know the resource exists
	return append(warnings.Get(), resourceComputeRead(ctx, d, m)...)
}

func resourceComputeRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceComputeRead: called for Compute name %s, RG ID %d",
		d.Get("name").(string), d.Get("rg_id").(int))

	// c := m.(*controller.ControllerCfg)

	computeRec, err := utilityComputeCheckPresence(ctx, d, m)
	if err != nil {
		d.SetId("")
		return diag.FromErr(err)
	}

	pciList, err := utilityComputePCIDevicesList(ctx, d, m)
	if err != nil {
		return diag.FromErr(err)
	}

	hasChanged := false

	switch computeRec.Status {
	case status.Deleted:
		// restoreReq := compute.RestoreRequest{ComputeID: computeRec.ID}
		// enableReq := compute.EnableRequest{ComputeID: computeRec.ID}

		// _, err := c.CloudAPI().Compute().Restore(ctx, restoreReq)
		// if err != nil {
		// 	return diag.FromErr(err)
		// }

		// _, err = c.CloudAPI().Compute().Enable(ctx, enableReq)
		// if err != nil {
		// 	return diag.FromErr(err)
		// }

		// hasChanged = true
	case status.Destroyed:
		d.SetId("")
		return diag.Errorf("The resource cannot be updated because it has been destroyed")
		// return resourceComputeCreate(ctx, d, m)
	case status.Disabled:
		log.Debugf("The compute is in status: %s, troubles may occur with update. Please, enable compute first.", computeRec.Status)
	case status.Redeploying:
	case status.Deleting:
	case status.Destroying:
		return diag.Errorf("The compute is in progress with status: %s", computeRec.Status)
	case status.Modeled:
		return diag.Errorf("The compute is in status: %s, please, contact support for more information", computeRec.Status)
	}

	if hasChanged {
		computeRec, err = utilityComputeCheckPresence(ctx, d, m)
		if err != nil {
			d.SetId("")
			return diag.FromErr(err)
		}

		pciList, err = utilityComputePCIDevicesList(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if err = flattenCompute(d, computeRec, pciList); err != nil {
		return diag.FromErr(err)
	}

	log.Debugf("resourceComputeRead: after flattenCompute: Compute ID %s, name %q, RG ID %d",
		d.Id(), d.Get("name").(string), d.Get("rg_id").(int))

	return nil
}

func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceComputeUpdate: called for Compute ID %s / name %s, RGID %d",
		d.Id(), d.Get("name").(string), d.Get("rg_id").(int))

	c := m.(*controller.ControllerCfg)

	hasRG, err := existRgID(ctx, d, m)
	if err != nil {
		return diag.FromErr(err)
	}

	if !hasRG {
		return diag.Errorf("resourceComputeUpdate: can't update Compute because rgID %d not allowed or does not exist", d.Get("rg_id").(int))
	}

	if !d.Get("create_blank").(bool) {
		hasImage, err := existImageId(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}

		if !hasImage {
			return diag.Errorf("resourceComputeUpdate: can't update Compute because imageID %d not allowed or does not exist", d.Get("image_id").(int))
		}
	}

	if d.HasChange("zone_id") {
		if err := utilityComputeUpdateZoneID(ctx, d, m); err != nil {
			return diag.FromErr(err)
		}
	}

	if network, ok := d.GetOk("network"); ok {
		networkList := network.(*schema.Set).List()
		for _, elem := range networkList {
			networkData := elem.(map[string]interface{})

			switch networkData["net_type"].(string) {
			case "VINS":
				if vinsId, ok := existVinsId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeUpdate: can't update compute because vins ID %d is not allowed or does not exist", vinsId)
				}
			case "EXTNET":
				if extNetId, ok := existExtNetId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeUpdate: can't update compute because extnet ID %d is not allowed or does not exist", extNetId)
				}
			case "VFNIC":
				if vfpoolId, ok := existVFPoolId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeUpdate: can't create compute because vfpool ID %d is not allowed or does not exist", vfpoolId)
				}
			case "DPDK":
				if dpdkId, ok := existDPDKNetId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeCreate: can't create compute because DPDK ID %d is not allowed or does not exist", dpdkId)
				}
			case "TRUNK":
				if trunkId, ok := existTRUNKId(ctx, m, networkData["net_id"].(int)); !ok {
					return diag.Errorf("resourceComputeCreate: can't create compute because TRUNK ID %d is not allowed or does not exist", trunkId)
				}
			default:
				continue
			}
		}
	}

	computeRec, err := utilityComputeCheckPresence(ctx, d, m)
	if err != nil {
		return diag.FromErr(err)
	}

	hasChanged := false

	// check compute statuses
	switch computeRec.Status {
	case status.Deleted:
		if restore, ok := d.GetOk("restore"); ok && restore.(bool) {
			restoreReq := compute.RestoreRequest{ComputeID: computeRec.ID}
			_, err := c.CloudAPI().Compute().Restore(ctx, restoreReq)
			if err != nil {
				d.SetId("")
				return diag.FromErr(err)
			}
		}

		if enabled, ok := d.GetOk("enabled"); ok {
			if enabled.(bool) {
				enableReq := compute.EnableRequest{ComputeID: computeRec.ID}
				_, err = c.CloudAPI().Compute().Enable(ctx, enableReq)
				if err != nil {
					return diag.FromErr(err)
				}
			}
			if !enabled.(bool) {
				enableReq := compute.DisableRequest{ComputeID: computeRec.ID}
				_, err = c.CloudAPI().Compute().Disable(ctx, enableReq)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}

		if start, ok := d.GetOk("started"); ok {
			if start.(bool) {
				req := compute.StartRequest{ComputeID: computeRec.ID}

				if altBootID, ok := d.Get("alt_boot_id").(int); ok {
					req.AltBootID = uint64(altBootID)
				}

				if _, err := c.CloudAPI().Compute().Start(ctx, req); err != nil {
					return diag.FromErr(err)
				}
			}
			if !start.(bool) {
				req := compute.StopRequest{ComputeID: computeRec.ID}

				if _, err := c.CloudAPI().Compute().Stop(ctx, req); err != nil {
					return diag.FromErr(err)
				}
			}
		}

		hasChanged = true
	case status.Destroyed:
		d.SetId("")
		return diag.Errorf("The resource cannot be updated because it has been destroyed")
		// return resourceComputeCreate(ctx, d, m)
	case status.Disabled:
		log.Debugf("The compute is in status: %s, may troubles can be occured with update. Please, enable compute first.", computeRec.Status)
	case status.Redeploying:
	case status.Deleting:
	case status.Destroying:
		return diag.Errorf("The compute is in progress with status: %s", computeRec.Status)
	case status.Modeled:
		return diag.Errorf("The compute is in status: %s, please, contant the support for more information", computeRec.Status)
	}

	if hasChanged {
		computeRec, err = utilityComputeCheckPresence(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("enabled") {
		enabled := d.Get("enabled").(bool)
		if enabled {
			req := compute.EnableRequest{
				ComputeID: computeRec.ID,
			}

			if _, err := c.CloudAPI().Compute().Enable(ctx, req); err != nil {
				return diag.FromErr(err)
			}
		} else {
			req := compute.DisableRequest{
				ComputeID: computeRec.ID,
			}

			if _, err := c.CloudAPI().Compute().Disable(ctx, req); err != nil {
				return diag.FromErr(err)
			}
		}
		log.Debugf("resourceComputeUpdate: enable=%s Compute ID %v after completing its resource configuration", d.Id(), enabled)
	}

	oldSize, newSize := d.GetChange("boot_disk_size")
	if oldSize.(int) < newSize.(int) {
		req := compute.DiskResizeRequest{ComputeID: computeRec.ID}
		if diskId, ok := d.GetOk("boot_disk_id"); ok {
			req.DiskID = uint64(diskId.(int))

		} else {
			bootDisk, err := utilityComputeBootDiskCheckPresence(ctx, d, m)
			if err != nil {
				return diag.FromErr(err)
			}

			req.DiskID = bootDisk.ID
		}
		req.Size = uint64(newSize.(int))

		log.Debugf("resourceComputeUpdate: compute ID %s, boot disk ID %d resize %d -> %d",
			d.Id(), d.Get("boot_disk_id").(int), oldSize.(int), newSize.(int))

		_, err := c.CloudAPI().Compute().DiskResize(ctx, req)
		if err != nil {
			return diag.FromErr(err)
		}

	} else if oldSize.(int) > newSize.(int) {
		log.Warnf("resourceComputeUpdate: compute ID %s - shrinking boot disk is not allowed", d.Id())
	}

	if d.HasChange("extra_disks") {
		err := utilityComputeExtraDisksConfigure(ctx, d, m, true, computeRec.Disks) // pass do_delta = true to apply changes, if any
		if err != nil {
			return diag.FromErr(err)
		}
	}

	// Note bene: numa_affinity, cpu_pin and hp_backed are not allowed to be changed for compute in STARTED tech status.
	var isStopRequired bool
	if d.HasChanges("numa_affinity", "cpu_pin", "hp_backed", "chipset", "preferred_cpu", "hot_resize") && d.Get("started").(bool) {
		isStopRequired = true
	}

	old, new := d.GetChange("cpu")
	if old.(int) > new.(int) && d.Get("started").(bool) && d.Get("force_resize").(bool) {
		isStopRequired = true
	}
	if isStopRequired {
		if _, err := c.CloudAPI().Compute().Stop(ctx, compute.StopRequest{ComputeID: computeRec.ID}); err != nil {
			return diag.FromErr(err)
		}
	}

	doUpdate := false
	resizeReq := compute.ResizeRequest{
		ComputeID: computeRec.ID,
	}
	forceResize, ok := d.GetOk("force_resize")
	if ok {
		resizeReq.Force = forceResize.(bool)
	}

	warnings := dc.Warnings{}

	oldCpu, newCpu := d.GetChange("cpu")
	if oldCpu.(int) > newCpu.(int) && !forceResize.(bool) {
		return diag.Errorf("Cannot resize compute ID %d: enable 'force_resize' to reduce compute vCPUs", computeRec.ID)
	}
	if oldCpu.(int) != newCpu.(int) {
		resizeReq.CPU = uint64(newCpu.(int))
		doUpdate = true
	} else {
		resizeReq.CPU = 0
	}

	if resizeReq.CPU != 0 {
		if preferredCPU, ok := d.GetOk("preferred_cpu"); ok {
			preferredList := preferredCPU.([]interface{})
			if len(preferredList) > 0 {
				for _, v := range preferredList {
					cpuNum := v.(int)
					resizeReq.PreferredCPU = append(resizeReq.PreferredCPU, int64(cpuNum))
				}
			}
		}
		oldPCPU, newPCPU := d.GetChange("preferred_cpu")
		if len(oldPCPU.([]interface{})) != 0 && len(newPCPU.([]interface{})) == 0 {
			resizeReq.PreferredCPU = []int64{-1}
		}
	}

	oldRam, newRam := d.GetChange("ram")
	if oldRam.(int) != newRam.(int) {
		resizeReq.RAM = uint64(newRam.(int))
		doUpdate = true
	} else {
		resizeReq.RAM = 0
	}

	if doUpdate {
		log.Debugf("resourceComputeUpdate: changing CPU %d -> %d and/or RAM %d -> %d",
			oldCpu.(int), newCpu.(int),
			oldRam.(int), newRam.(int))
		_, err := c.CloudAPI().Compute().Resize(ctx, resizeReq)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChanges("description",
		"name",
		"numa_affinity",
		"cpu_pin",
		"hp_backed",
		"chipset",
		"auto_start_w_node",
		"preferred_cpu",
		"loader_type",
		"boot_type",
		"hot_resize",
		"network_interface_naming",
		"os_version") {
		req := compute.UpdateRequest{
			ComputeID: computeRec.ID,
		}

		if d.HasChange("name") {
			req.Name = d.Get("name").(string)
		}
		if d.HasChange("description") {
			req.Description = d.Get("description").(string)
		}
		if d.HasChange("numa_affinity") {
			req.NumaAffinity = d.Get("numa_affinity").(string)
		}
		if d.HasChange("chipset") {
			req.Chipset = d.Get("chipset").(string)
		}
		if d.HasChange("preferred_cpu") {
			if preferredCPU, ok := d.GetOk("preferred_cpu"); ok {
				preferredList := preferredCPU.([]interface{})
				if len(preferredList) > 0 {
					for _, v := range preferredList {
						cpuNum := v.(int)
						req.PreferredCPU = append(req.PreferredCPU, int64(cpuNum))
					}
				}
			}
			oldPCPU, newPCPU := d.GetChange("preferred_cpu")
			if len(oldPCPU.([]interface{})) != 0 && len(newPCPU.([]interface{})) == 0 {
				req.PreferredCPU = []int64{-1}
			}
		}
		req.CPUPin = d.Get("cpu_pin").(bool)
		req.HPBacked = d.Get("hp_backed").(bool)
		req.AutoStart = d.Get("auto_start_w_node").(bool)

		if d.HasChange("loader_type") {
			req.LoaderType = d.Get("loader_type").(string)
		}

		if d.HasChange("boot_type") {
			req.BootType = d.Get("boot_type").(string)
		}

		if d.HasChange("hot_resize") {
			req.HotResize = d.Get("hot_resize").(bool)
		}

		if d.HasChange("network_interface_naming") {
			req.NetworkInterfaceNaming = d.Get("network_interface_naming").(string)
		}

		if d.HasChange("os_version") {
			req.OSVersion = d.Get("os_version").(string)
		}

		// perform update
		if _, err := c.CloudAPI().Compute().Update(ctx, req); err != nil {
			return diag.FromErr(err)
		}

	}

	// If used to be STARTED, we need to start it after update
	if isStopRequired {

		req := compute.StartRequest{ComputeID: computeRec.ID}
		if altBootID, ok := d.Get("alt_boot_id").(int); ok {
			req.AltBootID = uint64(altBootID)
		}
		if _, err := c.CloudAPI().Compute().Start(ctx, req); err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("network") {
		err = utilityComputeNetworksConfigure(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("security_groups") {
		err = utilityComputeSecGroupsConfigure(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("disks") {
		deletedDisks := make([]interface{}, 0)
		addedDisks := make([]interface{}, 0)
		resizedDisks := make([]interface{}, 0)
		renamedDisks := make([]interface{}, 0)
		changeStoragePolicyDisks := make([]interface{}, 0)
		iotuneUpdatedDisks := make([]interface{}, 0)

		oldDisks, newDisks := d.GetChange("disks")
		oldConv := oldDisks.([]interface{})
		newConv := newDisks.([]interface{})

		for _, el := range oldConv {
			if !isContainsDisk(newConv, el) && !isRenameDisk(newConv, el) && !isResizeDisk(newConv, el) && !isChangeStoragePolicy(newConv, el) {
				flag := false
				extraDisks := d.Get("extra_disks").(*schema.Set).List()
				delDisk := el.(map[string]interface{})
				delDiskId := delDisk["disk_id"].(int)

				for _, extraDiskId := range extraDisks {
					if extraDiskId.(int) == delDiskId {
						flag = true
						break
					}
				}

				if !flag {
					deletedDisks = append(deletedDisks, el)
				} else {
					log.Debugf("disk %d will not be deleted because it is present in the extra_disks block", delDiskId)
				}
			}
		}

		for _, el := range newConv {
			if !isContainsDisk(oldConv, el) {
				addedDisks = append(addedDisks, el)
			}
			if isResizeDisk(oldConv, el) {
				resizedDisks = append(resizedDisks, el)
			}
			if isRenameDisk(oldConv, el) {
				renamedDisks = append(renamedDisks, el)
			}
			if isChangeStoragePolicy(oldConv, el) {
				changeStoragePolicyDisks = append(changeStoragePolicyDisks, el)
			}
			if isChangeIOTuneDisk(oldConv, el) {
				iotuneUpdatedDisks = append(iotuneUpdatedDisks, el)
			}
		}

		chipset := d.Get("chipset").(string)

		if len(deletedDisks) > 0 {
			for _, disk := range deletedDisks {
				diskConv := disk.(map[string]interface{})
				if isBootDisk(diskConv, chipset) {
					continue
				}

				req := compute.DiskDelRequest{
					ComputeID:   computeRec.ID,
					DiskID:      uint64(diskConv["disk_id"].(int)),
					Permanently: diskConv["permanently"].(bool),
				}

				log.Debugf("trying to delete disk %d", req.DiskID)

				_, err := c.CloudAPI().Compute().DiskDel(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}

		if len(addedDisks) > 0 {
			for _, disk := range addedDisks {
				diskConv := disk.(map[string]interface{})
				if isBootDisk(diskConv, chipset) {
					continue
				}
				req := compute.DiskAddRequest{
					ComputeID:       computeRec.ID,
					DiskName:        diskConv["disk_name"].(string),
					Size:            uint64(diskConv["size"].(int)),
					StoragePolicyID: uint64(diskConv["storage_policy_id"].(int)),
				}

				if diskConv["sep_id"].(int) != 0 {
					req.SepID = uint64(diskConv["sep_id"].(int))
				}
				if diskConv["pool"].(string) != "" {
					req.Pool = diskConv["pool"].(string)
				}
				if diskConv["desc"].(string) != "" {
					req.Description = diskConv["desc"].(string)
				}
				if diskConv["image_id"].(int) != 0 {
					req.ImageID = uint64(diskConv["image_id"].(int))
				}
				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)
					}
				}
			}
		}

		if len(resizedDisks) > 0 {
			for _, disk := range resizedDisks {
				diskConv := disk.(map[string]interface{})
				if isBootDisk(diskConv, chipset) {
					continue
				}
				req := compute.DiskResizeRequest{
					ComputeID: computeRec.ID,
					DiskID:    uint64(diskConv["disk_id"].(int)),
					Size:      uint64(diskConv["size"].(int)),
				}

				_, err := c.CloudAPI().Compute().DiskResize(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}

		if len(renamedDisks) > 0 {
			for _, disk := range renamedDisks {
				diskConv := disk.(map[string]interface{})

				req := disks.RenameRequest{
					DiskID: uint64(diskConv["disk_id"].(int)),
					Name:   diskConv["disk_name"].(string),
				}

				_, err := c.CloudAPI().Disks().Rename(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}

		if len(changeStoragePolicyDisks) > 0 {
			for _, disk := range changeStoragePolicyDisks {
				diskConv := disk.(map[string]interface{})

				req := disks.ChangeDiskStoragePolicyRequest{
					DiskID:          uint64(diskConv["disk_id"].(int)),
					StoragePolicyID: uint64(diskConv["storage_policy_id"].(int)),
				}

				_, err := c.CloudAPI().Disks().ChangeDiskStoragePolicy(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}

		if len(iotuneUpdatedDisks) > 0 {
			for _, disk := range iotuneUpdatedDisks {
				diskConv := disk.(map[string]interface{})
				if isBootDisk(diskConv, chipset) {
					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.CloudAPI().Disks().LimitIO(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}
	}

	if d.HasChange("affinity_label") {
		affinityLabel := d.Get("affinity_label").(string)
		if affinityLabel == "" {
			req := compute.AffinityLabelRemoveRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().AffinityLabelRemove(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
		req := compute.AffinityLabelSetRequest{
			ComputeID:     computeRec.ID,
			AffinityLabel: affinityLabel,
		}

		_, err := c.CloudAPI().Compute().AffinityLabelSet(ctx, req)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("affinity_rules") {
		deletedAR := make([]interface{}, 0)
		addedAR := make([]interface{}, 0)

		oldAR, newAR := d.GetChange("affinity_rules")
		oldConv := oldAR.([]interface{})
		newConv := newAR.([]interface{})

		if len(newConv) == 0 {
			req := compute.AffinityRulesClearRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().AffinityRulesClear(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		} else {
			for _, el := range oldConv {
				if !isContainsAR(newConv, el) {
					deletedAR = append(deletedAR, el)
				}
			}
			for _, el := range newConv {
				if !isContainsAR(oldConv, el) {
					addedAR = append(addedAR, el)
				}
			}

			if len(deletedAR) > 0 {
				for _, ar := range deletedAR {
					arConv := ar.(map[string]interface{})
					req := compute.AffinityRuleRemoveRequest{
						ComputeID: computeRec.ID,
						Topology:  arConv["topology"].(string),
						Policy:    arConv["policy"].(string),
						Mode:      arConv["mode"].(string),
						Key:       arConv["key"].(string),
						Value:     arConv["value"].(string),
					}

					_, err := c.CloudAPI().Compute().AffinityRuleRemove(ctx, req)
					if err != nil {
						return diag.FromErr(err)
					}
				}
			}
			if len(addedAR) > 0 {
				for _, ar := range addedAR {
					arConv := ar.(map[string]interface{})
					req := compute.AffinityRuleAddRequest{
						ComputeID: computeRec.ID,
						Topology:  arConv["topology"].(string),
						Policy:    arConv["policy"].(string),
						Mode:      arConv["mode"].(string),
						Key:       arConv["key"].(string),
						Value:     arConv["value"].(string),
					}

					_, err := c.CloudAPI().Compute().AffinityRuleAdd(ctx, req)
					if err != nil {
						return diag.FromErr(err)
					}
				}
			}
		}

	}

	if d.HasChange("anti_affinity_rules") {
		deletedAR := make([]interface{}, 0)
		addedAR := make([]interface{}, 0)

		oldAR, newAR := d.GetChange("anti_affinity_rules")
		oldConv := oldAR.([]interface{})
		newConv := newAR.([]interface{})

		if len(newConv) == 0 {
			req := compute.AntiAffinityRulesClearRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().AntiAffinityRulesClear(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		} else {
			for _, el := range oldConv {
				if !isContainsAR(newConv, el) {
					deletedAR = append(deletedAR, el)
				}
			}
			for _, el := range newConv {
				if !isContainsAR(oldConv, el) {
					addedAR = append(addedAR, el)
				}
			}

			if len(deletedAR) > 0 {
				for _, ar := range deletedAR {
					arConv := ar.(map[string]interface{})
					req := compute.AntiAffinityRuleRemoveRequest{
						ComputeID: computeRec.ID,
						Topology:  arConv["topology"].(string),
						Policy:    arConv["policy"].(string),
						Mode:      arConv["mode"].(string),
						Key:       arConv["key"].(string),
						Value:     arConv["value"].(string),
					}

					_, err := c.CloudAPI().Compute().AntiAffinityRuleRemove(ctx, req)
					if err != nil {
						return diag.FromErr(err)
					}
				}
			}
			if len(addedAR) > 0 {
				for _, ar := range addedAR {
					arConv := ar.(map[string]interface{})
					req := compute.AntiAffinityRuleAddRequest{
						ComputeID: computeRec.ID,
						Topology:  arConv["topology"].(string),
						Policy:    arConv["policy"].(string),
						Mode:      arConv["mode"].(string),
						Key:       arConv["key"].(string),
						Value:     arConv["value"].(string),
					}

					_, err := c.CloudAPI().Compute().AntiAffinityRuleAdd(ctx, req)
					if err != nil {
						return diag.FromErr(err)
					}
				}
			}
		}

	}

	if d.HasChange("tags") {
		oldSet, newSet := d.GetChange("tags")
		deletedTags := (oldSet.(*schema.Set).Difference(newSet.(*schema.Set))).List()
		if len(deletedTags) > 0 {
			for _, tagInterface := range deletedTags {
				tagItem := tagInterface.(map[string]interface{})
				req := compute.TagRemoveRequest{
					ComputeID: computeRec.ID,
					Key:       tagItem["key"].(string),
				}

				_, err := c.CloudAPI().Compute().TagRemove(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}

		addedTags := (newSet.(*schema.Set).Difference(oldSet.(*schema.Set))).List()
		if len(addedTags) > 0 {
			for _, tagInterface := range addedTags {
				tagItem := tagInterface.(map[string]interface{})
				req := compute.TagAddRequest{
					ComputeID: computeRec.ID,
					Key:       tagItem["key"].(string),
					Value:     tagItem["value"].(string),
				}

				_, err := c.CloudAPI().Compute().TagAdd(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}
	}

	if d.HasChange("port_forwarding") {
		oldSet, newSet := d.GetChange("port_forwarding")
		deletedPfws := (oldSet.(*schema.Set).Difference(newSet.(*schema.Set))).List()
		if len(deletedPfws) > 0 {
			for _, pfwInterface := range deletedPfws {
				pfwItem := pfwInterface.(map[string]interface{})
				req := compute.PFWDelRequest{
					ComputeID:       computeRec.ID,
					PublicPortStart: uint64(pfwItem["public_port_start"].(int)),
					Proto:           pfwItem["proto"].(string),
				}
				if pfwItem["local_port"].(int) != 0 {
					req.LocalBasePort = uint64(pfwItem["local_port"].(int))
				}
				if pfwItem["public_port_end"].(int) == -1 {
					req.PublicPortEnd = req.PublicPortStart
				} else {
					req.PublicPortEnd = uint64(pfwItem["public_port_end"].(int))
				}

				_, err := c.CloudAPI().Compute().PFWDel(ctx, req)
				if err != nil {
					warnings.Add(err)
				}
			}
		}

		addedPfws := (newSet.(*schema.Set).Difference(oldSet.(*schema.Set))).List()
		if len(addedPfws) > 0 {
			for _, pfwInterface := range addedPfws {
				pfwItem := pfwInterface.(map[string]interface{})
				req := compute.PFWAddRequest{
					ComputeID:       computeRec.ID,
					PublicPortStart: uint64(pfwItem["public_port_start"].(int)),
					PublicPortEnd:   int64(pfwItem["public_port_end"].(int)),
					Proto:           pfwItem["proto"].(string),
				}
				if pfwItem["local_port"].(int) != 0 {
					req.LocalBasePort = uint64(pfwItem["local_port"].(int))
				}
				_, err := c.CloudAPI().Compute().PFWAdd(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}
	}

	if d.HasChange("user_access") {
		oldSet, newSet := d.GetChange("user_access")
		deletedUserAcess := (oldSet.(*schema.Set).Difference(newSet.(*schema.Set))).List()
		if len(deletedUserAcess) > 0 {
			for _, userAcessInterface := range deletedUserAcess {
				userAccessItem := userAcessInterface.(map[string]interface{})
				req := compute.UserRevokeRequest{
					ComputeID: computeRec.ID,
					Username:  userAccessItem["username"].(string),
				}

				_, err := c.CloudAPI().Compute().UserRevoke(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}

		addedUserAccess := (newSet.(*schema.Set).Difference(oldSet.(*schema.Set))).List()
		if len(addedUserAccess) > 0 {
			for _, userAccessInterface := range addedUserAccess {
				userAccessItem := userAccessInterface.(map[string]interface{})
				req := compute.UserGrantRequest{
					ComputeID:  computeRec.ID,
					Username:   userAccessItem["username"].(string),
					AccessType: userAccessItem["access_type"].(string),
				}

				_, err := c.CloudAPI().Compute().UserGrant(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}
	}

	if d.HasChange("snapshot") {
		oldSet, newSet := d.GetChange("snapshot")
		deletedSnapshots := (oldSet.(*schema.Set).Difference(newSet.(*schema.Set))).List()
		if len(deletedSnapshots) > 0 {
			for _, snapshotInterface := range deletedSnapshots {
				snapshotItem := snapshotInterface.(map[string]interface{})
				req := compute.SnapshotDeleteRequest{
					ComputeID: computeRec.ID,
					Label:     snapshotItem["label"].(string),
				}

				asyncMode, ok := d.GetOk("snapshot_delete_async")
				if ok && asyncMode.(bool) {
					_, err := c.CloudAPI().Compute().SnapshotDeleteAsync(ctx, req)
					if err != nil {
						return diag.FromErr(err)
					}
				} else {
					_, err := c.CloudAPI().Compute().SnapshotDelete(ctx, req)
					if err != nil {
						return diag.FromErr(err)
					}
				}

			}
		}

		addedSnapshots := (newSet.(*schema.Set).Difference(oldSet.(*schema.Set))).List()
		if len(addedSnapshots) > 0 {
			for _, snapshotInterface := range addedSnapshots {
				snapshotItem := snapshotInterface.(map[string]interface{})
				req := compute.SnapshotCreateRequest{
					ComputeID: computeRec.ID,
					Label:     snapshotItem["label"].(string),
				}

				_, err := c.CloudAPI().Compute().SnapshotCreate(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}
	}

	if d.HasChange("rollback") {
		if rollback, ok := d.GetOk("rollback"); ok {
			req := compute.StopRequest{
				ComputeID: computeRec.ID,
				Force:     false,
			}

			_, err := c.CloudAPI().Compute().Stop(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}

			rollbackInterface := rollback.(*schema.Set).List()[0]
			rollbackItem := rollbackInterface.(map[string]interface{})

			rollbackReq := compute.SnapshotRollbackRequest{
				ComputeID: computeRec.ID,
				Label:     rollbackItem["label"].(string),
			}

			_, err = c.CloudAPI().Compute().SnapshotRollback(ctx, rollbackReq)
			if err != nil {
				return diag.FromErr(err)
			}
		}
	}

	if d.HasChange("cd") {
		oldSet, newSet := d.GetChange("cd")
		deletedCd := (oldSet.(*schema.Set).Difference(newSet.(*schema.Set))).List()
		if len(deletedCd) > 0 {
			req := compute.CDEjectRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().CDEject(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}

		addedCd := (newSet.(*schema.Set).Difference(oldSet.(*schema.Set))).List()
		if len(addedCd) > 0 {
			cdItem := addedCd[0].(map[string]interface{})
			req := compute.CDInsertRequest{
				ComputeID: computeRec.ID,
				CDROMID:   uint64(cdItem["cdrom_id"].(int)),
			}

			_, err := c.CloudAPI().Compute().CDInsert(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
	}

	if d.HasChange("pause") {
		oldPause, newPause := d.GetChange("pause")
		if !newPause.(bool) {
			req := compute.ResumeRequest{
				ComputeID: computeRec.ID,
			}
			_, err := c.CloudAPI().Compute().Resume(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
		if !oldPause.(bool) {
			req := compute.PauseRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().Pause(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
	}

	if d.HasChange("reset") {
		_, newReset := d.GetChange("reset")
		if newReset.(bool) {
			req := compute.ResetRequest{
				ComputeID: computeRec.ID,
			}
			_, err := c.CloudAPI().Compute().Reset(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
	}

	if d.HasChange("image_id") {
		oldImage, newImage := d.GetChange("image_id")
		stopReq := compute.StopRequest{
			ComputeID: computeRec.ID,
			Force:     false,
		}
		if forceStop, ok := d.GetOk("force_stop"); ok {
			stopReq.Force = forceStop.(bool)
		}

		_, err := c.CloudAPI().Compute().Stop(ctx, stopReq)
		if err != nil {
			return diag.FromErr(err)
		}

		if oldImage.(int) != newImage.(int) {
			req := compute.RedeployRequest{
				ComputeID:       computeRec.ID,
				StoragePolicyID: uint64(d.Get("storage_policy_id").(int)),
				ImageID:         uint64(newImage.(int)),
				DataDisks:       "KEEP",
			}

			if diskSize, ok := d.GetOk("boot_disk_size"); ok {
				req.DiskSize = uint64(diskSize.(int))
			}
			if autoStart, ok := d.GetOk("started"); ok {
				req.AutoStart = autoStart.(bool)
			}
			if forceStop, ok := d.GetOk("force_stop"); ok {
				req.ForceStop = forceStop.(bool)
			}

			if osVersion, ok := d.GetOk("os_version"); ok {
				req.OSVersion = osVersion.(string)
			}

			_, err := c.CloudAPI().Compute().Redeploy(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
	}

	if d.HasChange("custom_fields") {
		val := d.Get("custom_fields").(string)
		val = strings.ReplaceAll(val, "\\", "")
		val = strings.ReplaceAll(val, "\n", "")
		val = strings.ReplaceAll(val, "\t", "")
		val = strings.TrimSpace(val)

		if len(val) > 0 {
			req := compute.SetCustomFieldsRequest{
				ComputeID:    computeRec.ID,
				CustomFields: val,
			}

			_, err := c.CloudAPI().Compute().SetCustomFields(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		} else {
			req := compute.DeleteCustomFieldsRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().DeleteCustomFields(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
	}

	if d.HasChange("pci_devices") {
		if err := utilityComputeUpdatePciDevices(ctx, d, m); err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("started") {
		if d.Get("started").(bool) {
			req := compute.StartRequest{
				ComputeID: computeRec.ID,
			}
			if altBootID, ok := d.Get("alt_boot_id").(int); ok {
				req.AltBootID = uint64(altBootID)
			}
			if !isStopRequired {
				if _, err := c.CloudAPI().Compute().Start(ctx, req); err != nil {
					return diag.FromErr(err)
				}
			}

		} else {
			req := compute.StopRequest{
				ComputeID: computeRec.ID,
			}
			if _, err := c.CloudAPI().Compute().Stop(ctx, req); err != nil {
				return diag.FromErr(err)
			}
		}
	}

	if d.HasChange("pin_to_node") || d.HasChange("started") {
		newPin := d.Get("pin_to_node")
		if !newPin.(bool) && d.HasChange("pin_to_node") {
			req := compute.UnpinFromNodeRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().UnpinFromNode(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
		if newPin.(bool) {
			if !d.Get("started").(bool) {
				return diag.Errorf("Cannot pin to node a VM, that is not started")
			}

			req := compute.PinToNodeRequest{
				ComputeID: computeRec.ID,
			}
			req.AutoStart = d.Get("auto_start_w_node").(bool)
			_, err = c.CloudAPI().Compute().PinToNode(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
	}

	// we may reuse dataSourceComputeRead here as we maintain similarity
	// between Compute resource and Compute data source schemas

	return append(warnings.Get(), resourceComputeRead(ctx, d, m)...)
}

func isResizeDisk(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) &&
			elOldConv["size"].(int) != elConv["size"].(int) {
			return true
		}
	}
	return false
}

func isRenameDisk(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) &&
			elOldConv["disk_name"].(string) != elConv["disk_name"].(string) {
			return true
		}
	}
	return false
}

func isChangeStoragePolicy(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) &&
			elOldConv["storage_policy_id"].(int) != elConv["storage_policy_id"].(int) {
			return true
		}
	}
	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 isContainsDisk(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) {
			return true
		}
	}
	return false
}

func isContainsAR(els []interface{}, el interface{}) bool {
	for _, elOld := range els {
		elOldConv := elOld.(map[string]interface{})
		elConv := el.(map[string]interface{})
		if elOldConv["key"].(string) == elConv["key"].(string) &&
			elOldConv["value"].(string) == elConv["value"].(string) &&
			elOldConv["mode"].(string) == elConv["mode"].(string) &&
			elOldConv["topology"].(string) == elConv["topology"].(string) &&
			elOldConv["policy"].(string) == elConv["policy"].(string) {
			return true
		}
	}
	return false
}

func resourceComputeDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	// NOTE: this function destroys target Compute instance "permanently", so
	// there is no way to restore it.
	// If compute being destroyed has some extra disks attached, they are
	// detached from the compute
	log.Debugf("resourceComputeDelete: called for Compute name %s, RG ID %d",
		d.Get("name").(string), d.Get("rg_id").(int))

	c := m.(*controller.ControllerCfg)
	computeId, _ := strconv.ParseUint(d.Id(), 10, 64)

	req := compute.DeleteRequest{
		ComputeID:   computeId,
		Permanently: d.Get("permanently").(bool),
		DetachDisks: d.Get("detach_disks").(bool),
	}

	if _, err := c.CloudAPI().Compute().Delete(ctx, req); err != nil {
		return diag.FromErr(err)
	}

	d.SetId("")

	return nil
}

func disksSubresourceSchemaMake() map[string]*schema.Schema {
	rets := map[string]*schema.Schema{
		"disk_name": {
			Type:        schema.TypeString,
			Required:    true,
			Description: "Name for disk",
		},
		"size": {
			Type:        schema.TypeInt,
			Required:    true,
			Description: "Disk size in GiB",
		},
		"storage_policy_id": {
			Type:        schema.TypeInt,
			Required:    true,
			Description: "Storage policy id of disk. The rules of the specified storage policy will be used.",
		},
		"sep_id": {
			Type:        schema.TypeInt,
			Computed:    true,
			Optional:    true,
			Description: "Storage endpoint provider ID; by default the same with boot disk",
		},
		"pci_slot": {
			Type:        schema.TypeInt,
			Computed:    true,
			Description: "PCI slot number of the disk",
		},
		"bus_number": {
			Type:        schema.TypeInt,
			Computed:    true,
			Description: "Bus number of the disk on virtual bus (6 = boot disk)",
		},
		"pool": {
			Type:        schema.TypeString,
			Computed:    true,
			Optional:    true,
			Description: "Pool name; by default will be chosen automatically",
		},
		"desc": {
			Type:        schema.TypeString,
			Computed:    true,
			Optional:    true,
			Description: "Optional description",
		},
		"image_id": {
			Type:        schema.TypeInt,
			Computed:    true,
			Optional:    true,
			Description: "Specify image id for create disk from template",
		},
		"permanently": {
			Type:        schema.TypeBool,
			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,
			Description: "Disk ID",
		},
		"independent": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"shareable": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"size_max": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"size_used": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"present_to": {
			Type:     schema.TypeMap,
			Computed: true,
			Elem: &schema.Schema{
				Type: schema.TypeInt,
			},
		},
		"to_clean": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"devicename": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"cache": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"created_by": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"created_time": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"deleted_by": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"deleted_time": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"updated_time": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"blk_discard": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"block_size": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"provision": {
			Type:     schema.TypeString,
			Computed: true,
		},
	}
	return rets
}

func tagsSubresourceSchemaMake() map[string]*schema.Schema {
	return map[string]*schema.Schema{
		"key": {
			Type:     schema.TypeString,
			Required: true,
		},
		"value": {
			Type:     schema.TypeString,
			Required: true,
		},
	}
}

func portForwardingSubresourceSchemaMake() map[string]*schema.Schema {
	return map[string]*schema.Schema{
		"public_port_start": {
			Type:     schema.TypeInt,
			Required: true,
		},
		"public_port_end": {
			Type:     schema.TypeInt,
			Optional: true,
			Default:  -1,
		},
		"local_port": {
			Type:     schema.TypeInt,
			Optional: true,
		},
		"proto": {
			Type:         schema.TypeString,
			Required:     true,
			ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false),
		},
	}
}

func userAccessSubresourceSchemaMake() map[string]*schema.Schema {
	return map[string]*schema.Schema{
		"username": {
			Type:     schema.TypeString,
			Required: true,
		},
		"access_type": {
			Type:     schema.TypeString,
			Required: true,
		},
	}
}

func snapshotSubresourceSchemaMake() map[string]*schema.Schema {
	return map[string]*schema.Schema{
		"label": {
			Type:     schema.TypeString,
			Required: true,
		},
	}
}

func snapshotRollbackSubresourceSchemaMake() map[string]*schema.Schema {
	return map[string]*schema.Schema{
		"label": {
			Type:     schema.TypeString,
			Required: true,
		},
	}
}

func cdSubresourceSchemaMake() map[string]*schema.Schema {
	return map[string]*schema.Schema{
		"cdrom_id": {
			Type:     schema.TypeInt,
			Required: true,
		},
	}
}

func ResourceComputeSchemaMake() map[string]*schema.Schema {
	rets := map[string]*schema.Schema{
		"name": {
			Type:        schema.TypeString,
			Required:    true,
			Description: "Name of this compute. Compute names are case sensitive and must be unique in the resource group.",
		},
		"rg_id": {
			Type:     schema.TypeInt,
			Required: true,
			// ForceNew:     true,
			ValidateFunc: validation.IntAtLeast(1),
			Description:  "ID of the resource group where this compute should be deployed.",
		},
		"cpu": {
			Type:         schema.TypeInt,
			Required:     true,
			ValidateFunc: validation.IntBetween(1, constants.MAX_CPUS_PER_COMPUTE),
			Description:  "Number of CPUs to allocate to this compute instance.",
		},
		"ram": {
			Type:     schema.TypeInt,
			Required: true,
			ValidateFunc: validation.All(
				validation.IntAtLeast(constants.MIN_RAM_PER_COMPUTE),
				validators.DivisibleBy(constants.RAM_DIVISIBILITY),
			),
			Description: "Amount of RAM in MB to allocate to this compute instance.",
		},
		"storage_policy_id": {
			Type:        schema.TypeInt,
			Required:    true,
			Description: "Storage policy id of compute. The rules of the specified storage policy will be used.",
		},
		"boot_type": {
			Type:         schema.TypeString,
			Optional:     true,
			Computed:     true,
			ValidateFunc: validation.StringInSlice([]string{"bios", "uefi"}, false),
			Description:  "Type of image upload.",
		},
		"image_id": {
			Type:     schema.TypeInt,
			Optional: true,
			//ForceNew:    true, //REDEPLOY
			Description: "ID of the OS image to base this compute instance on.",
		},
		"zone_id": {
			Type:     schema.TypeInt,
			Optional: true,
			Computed: true,
		},

		"chipset": {
			Type:         schema.TypeString,
			Optional:     true,
			Computed:     true,
			ValidateFunc: validation.StringInSlice([]string{"Q35", "i440fx"}, false), // observe case while validating
			Description:  "Type of the emulated system.",
		},
		"without_boot_disk": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "If True, the imageId, bootDisk, sepId, pool parameters are ignored and the compute is created without a boot disk in the stopped state.",
		},
		"create_blank": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "If True, the compute is created via kvmx86/createBlank endpoint (without OS image). The image_id field is not required in this case.",
		},
		"boot_disk_size": {
			Type:        schema.TypeInt,
			Optional:    true,
			Computed:    true,
			Description: "This compute instance boot disk size in GB. Make sure it is large enough to accomodate selected OS image.",
		},
		"affinity_label": {
			Type:        schema.TypeString,
			Optional:    true,
			Description: "Set affinity label for compute",
		},
		"affinity_rules": {
			Type:     schema.TypeList,
			Optional: true,
			Elem: &schema.Resource{
				Schema: map[string]*schema.Schema{
					"topology": {
						Type:         schema.TypeString,
						Required:     true,
						ValidateFunc: validation.StringInSlice([]string{"node", "compute"}, false),
						Description:  "compute or node, for whom rule applies",
					},
					"policy": {
						Type:         schema.TypeString,
						Required:     true,
						ValidateFunc: validation.StringInSlice([]string{"RECOMMENDED", "REQUIRED"}, false),
						Description:  "RECOMMENDED or REQUIRED, the degree of 'strictness' of this rule",
					},
					"mode": {
						Type:         schema.TypeString,
						Required:     true,
						ValidateFunc: validation.StringInSlice([]string{"EQ", "NE", "ANY"}, false),
						Description:  "EQ or NE or ANY - the comparison mode is 'value', recorded by the specified 'key'",
					},
					"key": {
						Type:        schema.TypeString,
						Required:    true,
						Description: "key that are taken into account when analyzing this rule will be identified",
					},
					"value": {
						Type:        schema.TypeString,
						Required:    true,
						Description: "value that must match the key to be taken into account when analyzing this rule",
					},
				},
			},
		},
		"anti_affinity_rules": {
			Type:     schema.TypeList,
			Optional: true,
			Elem: &schema.Resource{
				Schema: map[string]*schema.Schema{
					"topology": {
						Type:         schema.TypeString,
						Required:     true,
						ValidateFunc: validation.StringInSlice([]string{"node", "compute"}, false),
						Description:  "compute or node, for whom rule applies",
					},
					"policy": {
						Type:         schema.TypeString,
						Required:     true,
						ValidateFunc: validation.StringInSlice([]string{"RECOMMENDED", "REQUIRED"}, false),
						Description:  "RECOMMENDED or REQUIRED, the degree of 'strictness' of this rule",
					},
					"mode": {
						Type:         schema.TypeString,
						Required:     true,
						ValidateFunc: validation.StringInSlice([]string{"EQ", "NE", "ANY"}, false),
						Description:  "EQ or NE or ANY - the comparison mode is 'value', recorded by the specified 'key'",
					},
					"key": {
						Type:        schema.TypeString,
						Required:    true,
						Description: "key that are taken into account when analyzing this rule will be identified",
					},
					"value": {
						Type:        schema.TypeString,
						Required:    true,
						Description: "value that must match the key to be taken into account when analyzing this rule",
					},
				},
			},
		},
		"disks": {
			Type:     schema.TypeList,
			Optional: true,
			Elem: &schema.Resource{
				Schema: disksSubresourceSchemaMake(),
			},
		},
		"custom_fields": {
			Type:     schema.TypeString,
			Optional: true,
			Computed: true,
		},
		"boot_disk": {
			Type:     schema.TypeSet,
			Computed: true,
			Elem: &schema.Resource{
				Schema: disksSubresourceSchemaMake(),
			},
		},
		"boot_image_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"sep_id": {
			Type:     schema.TypeInt,
			Optional: true,
			Computed: true,
			// ForceNew:    true,
			Description: "ID of SEP to create bootDisk on. Uses image's sepId if not set.",
		},
		"pool": {
			Type:     schema.TypeString,
			Optional: true,
			Computed: true,
			// ForceNew:    true,
			Description: "Pool to use if sepId is set, can be also empty if needed to be chosen by system.",
		},
		"extra_disks": {
			Type:     schema.TypeSet,
			Optional: true,
			Computed: true,
			MaxItems: constants.MAX_EXTRA_DISKS_PER_COMPUTE,
			Elem: &schema.Schema{
				Type: schema.TypeInt,
			},
			Description: "Optional list of IDs of extra disks to attach to this compute. You may specify several extra disks.",
		},
		"network": {
			Type:     schema.TypeSet,
			Optional: true,
			MinItems: 1,
			Elem: &schema.Resource{
				Schema: networkSubresourceSchemaMake(),
			},
			Description: "Optional network connection(s) for this compute. You may specify several network blocks, one for each connection.",
		},
		"tags": {
			Type:     schema.TypeSet,
			Optional: true,
			Elem: &schema.Resource{
				Schema: tagsSubresourceSchemaMake(),
			},
		},
		"port_forwarding": {
			Type:     schema.TypeSet,
			Optional: true,
			Elem: &schema.Resource{
				Schema: portForwardingSubresourceSchemaMake(),
			},
		},
		"user_access": {
			Type:     schema.TypeSet,
			Optional: true,
			Elem: &schema.Resource{
				Schema: userAccessSubresourceSchemaMake(),
			},
		},
		"snapshot": {
			Type:     schema.TypeSet,
			Optional: true,
			Elem: &schema.Resource{
				Schema: snapshotSubresourceSchemaMake(),
			},
		},
		"snapshot_delete_async": {
			Type:     schema.TypeBool,
			Optional: true,
		},
		"rollback": {
			Type:     schema.TypeSet,
			MaxItems: 1,
			Optional: true,
			Elem: &schema.Resource{
				Schema: snapshotRollbackSubresourceSchemaMake(),
			},
		},
		"cd": {
			Type:     schema.TypeSet,
			Optional: true,
			MaxItems: 1,
			Elem: &schema.Resource{
				Schema: cdSubresourceSchemaMake(),
			},
		},
		"pin_to_node": {
			Type:     schema.TypeBool,
			Optional: true,
			Default:  false,
		},
		"description": {
			Type:        schema.TypeString,
			Optional:    true,
			Description: "Optional text description of this compute instance.",
		},
		"cloud_init": {
			Type:        schema.TypeString,
			Optional:    true,
			Description: "Optional cloud_init parameters. Applied when creating new compute instance only, ignored in all other cases.",
		},
		"enabled": {
			Type:        schema.TypeBool,
			Optional:    true,
			Computed:    true,
			Description: "If true - enable compute, else - disable",
		},
		"loader_type": {
			Type:         schema.TypeString,
			Optional:     true,
			Computed:     true,
			ValidateFunc: validation.StringInSlice([]string{"linux", "windows", "unknown"}, false),
			Description:  "Type of image vm.",
		},
		"hot_resize": {
			Type:        schema.TypeBool,
			Optional:    true,
			Computed:    true,
			Description: "Type of image vm.",
		},
		"pause": {
			Type:     schema.TypeBool,
			Optional: true,
			Default:  false,
		},
		"reset": {
			Type:     schema.TypeBool,
			Optional: true,
			Default:  false,
		},
		"restore": {
			Type:     schema.TypeBool,
			Optional: true,
			Default:  false,
		},
		"auto_start_w_node": {
			Type:        schema.TypeBool,
			Optional:    true,
			Computed:    true,
			Description: "Flag for start compute after node exits from MAINTENANCE state",
		},
		"force_stop": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "Flag for redeploy compute",
		},
		"force_resize": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "Flag for resize compute",
		},
		"alt_boot_id": {
			Type:        schema.TypeInt,
			Optional:    true,
			Description: "ID of CD-ROM live image to boot",
		},
		"started": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     true,
			Description: "Is compute started.",
		},
		"detach_disks": {
			Type:     schema.TypeBool,
			Optional: true,
			Default:  true,
		},
		"permanently": {
			Type:     schema.TypeBool,
			Optional: true,
			Default:  true,
		},
		"network_interface_naming": {
			Type:         schema.TypeString,
			Optional:     true,
			Computed:     true,
			ValidateFunc: validation.StringInSlice([]string{"eth", "ens"}, false),
			Description:  "Name of netfowrk interface.",
		},
		"numa_affinity": {
			Type:         schema.TypeString,
			Optional:     true,
			Default:      "none",
			ValidateFunc: validation.StringInSlice([]string{"none", "strict", "loose"}, false), // observe case while validating
			Description:  "Rule for VM placement with NUMA affinity.",
		},
		"cpu_pin": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node.",
		},
		"hp_backed": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node.",
		},
		"preferred_cpu": {
			Type:     schema.TypeList,
			Optional: true,
			Computed: true,
			Elem: &schema.Schema{
				Type: schema.TypeInt,
			},
			Description: "Recommended isolated CPUs. Field is ignored if compute.cpupin=False or compute.pinned=False",
		},
		"pci_devices": {
			Type:     schema.TypeSet,
			Optional: true,
			Computed: true,
			Elem: &schema.Schema{
				Type: schema.TypeInt,
			},
			Description: "ID of the connected pci devices",
		},

		"security_groups": {
			Type:     schema.TypeSet,
			Optional: true,
			Elem: &schema.Resource{
				Schema: map[string]*schema.Schema{
					"net_type": {
						Type:         schema.TypeString,
						Required:     true,
						StateFunc:    statefuncs.StateFuncToUpper,
						ValidateFunc: validation.StringInSlice([]string{"VINS", "EXTNET", "DPDK"}, false), // observe case while validating
						Description:  "Type of the network",
					},
					"net_id": {
						Type:        schema.TypeInt,
						Required:    true,
						Description: "ID of the network",
					},
					"security_groups": {
						Type:     schema.TypeSet,
						Required: true,
						Elem: &schema.Schema{
							Type: schema.TypeInt,
						},
					},
					"enable_secgroups": {
						Type:     schema.TypeBool,
						Optional: true,
						Default:  false,
					},
				},
			},
			Description: "list of security group IDs to apply to this interface",
		},
		"os_version": {
			Type:        schema.TypeString,
			Optional:    true,
			Computed:    true,
			Description: "the OS version installed on the VM",
		},

		// The rest are Compute properties, which are "computed" once it is created
		"account_id": {
			Type:        schema.TypeInt,
			Computed:    true,
			Description: "ID of the account this compute instance belongs to.",
		},
		"account_name": {
			Type:        schema.TypeString,
			Computed:    true,
			Description: "Name of the account this compute instance belongs to.",
		},
		"affinity_weight": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"weight": {
			Type:        schema.TypeInt,
			Computed:    true,
			Description: "Priority weight of the compute. Higher value means higher priority and later migration.",
		},
		"arch": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"boot_order": {
			Type:     schema.TypeList,
			Computed: true,
			Elem: &schema.Schema{
				Type: schema.TypeString,
			},
		},
		"boot_disk_id": {
			Type:        schema.TypeInt,
			Computed:    true,
			Description: "This compute instance boot disk ID.",
		},
		"cd_image_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"clone_reference": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"clones": {
			Type:     schema.TypeList,
			Computed: true,
			Elem: &schema.Schema{
				Type: schema.TypeInt,
			},
		},
		"computeci_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"created_by": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"created_time": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"deleted_by": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"deleted_time": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"devices": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"gid": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"driver": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"guid": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"compute_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"interfaces": {
			Type:     schema.TypeList,
			Computed: true,
			Elem: &schema.Resource{
				Schema: computeInterfacesSchemaMake(),
			},
		},
		"lock_status": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"manager_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"manager_type": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"migrationjob": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"milestones": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"need_reboot": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"numa_node_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"natable_vins_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"natable_vins_ip": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"natable_vins_name": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"natable_vins_network": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"natable_vins_network_name": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"os_users": {
			Type:     schema.TypeList,
			Computed: true,
			Elem: &schema.Resource{
				Schema: osUsersSubresourceSchemaMake(),
			},
			Description: "Guest OS users provisioned on this compute instance.",
		},
		"pinned": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"reference_id": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"registered": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"res_name": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"reserved_node_cpus": {
			Type:     schema.TypeList,
			Computed: true,
			Elem: &schema.Schema{
				Type: schema.TypeInt,
			},
		},
		"rg_name": {
			Type:        schema.TypeString,
			Computed:    true,
			Description: "Name of the resource group where this compute instance is located.",
		},
		"snap_sets": {
			Type:     schema.TypeList,
			Computed: true,
			Elem: &schema.Resource{
				Schema: computeSnapSetsSchemaMake(),
			},
		},
		"stateless_sep_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"stateless_sep_type": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"status": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"tech_status": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"updated_by": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"updated_time": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"user_managed": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"read_only": {
			Type:        schema.TypeBool,
			Computed:    true,
			Description: "Shows if compute is in read-only mode.",
		},
		"vnc_password": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"vgpus": {
			Type:        schema.TypeList,
			Computed:    true,
			Description: "List of virtual GPUs",
			Elem: &schema.Resource{
				Schema: map[string]*schema.Schema{
					"id": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"gid": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"type": {
						Type:     schema.TypeString,
						Computed: true,
					},
					"mode": {
						Type:     schema.TypeString,
						Computed: true,
					},
					"status": {
						Type:     schema.TypeString,
						Computed: true,
					},
					"profile_id": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"ram": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"last_update_time": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"created_time": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"deleted_time": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"vmid": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"pgpuid": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"reference_id": {
						Type:     schema.TypeString,
						Computed: true,
					},
					"account_id": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"rg_id": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"last_claimed_by": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"pci_slot": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"bus_number": {
						Type:     schema.TypeInt,
						Computed: true,
					},
					"guid": {
						Type:     schema.TypeInt,
						Computed: true,
					},
				},
			},
		},
		"virtual_image_id": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"virtual_image_name": {
			Type:     schema.TypeString,
			Computed: true,
		},
		"loader_meta_iso": {
			Type:     schema.TypeList,
			Computed: true,
			Elem: &schema.Resource{
				Schema: map[string]*schema.Schema{
					"device_name": {
						Type:     schema.TypeString,
						Computed: true,
					},
					"path": {
						Type:     schema.TypeString,
						Computed: true,
					},
				},
			},
		},
	}
	return rets
}

func ResourceCompute() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 3,

		CreateContext: resourceComputeCreate,
		ReadContext:   resourceComputeRead,
		UpdateContext: resourceComputeUpdate,
		DeleteContext: resourceComputeDelete,

		Importer: &schema.ResourceImporter{
			StateContext: schema.ImportStatePassthroughContext,
		},

		CustomizeDiff: func(ctx context.Context, diff *schema.ResourceDiff, i interface{}) error {
			if diff.HasChanges() || diff.HasChanges("chipset", "pin_to_node", "auto_start_w_node", "network", "affinity_rules", "anti_affinity_rules",
				"extra_disks", "tags", "port_forwarding", "user_access", "snapshot", "pci_devices", "preferred_cpu", "security_groups") {
				diff.SetNewComputed("updated_time")
				diff.SetNewComputed("updated_by")
			}
			if diff.HasChanges("pin_to_node") {
				diff.SetNewComputed("pinned")
			}
			if diff.HasChanges("image_id") {
				diff.SetNewComputed("boot_disk")
				diff.SetNewComputed("boot_image_id")
			}
			return nil
		},

		Timeouts: &schema.ResourceTimeout{
			Create:  &constants.Timeout600s,
			Read:    &constants.Timeout300s,
			Update:  &constants.Timeout300s,
			Delete:  &constants.Timeout300s,
			Default: &constants.Timeout300s,
		},

		Schema: ResourceComputeSchemaMake(),
		StateUpgraders: []schema.StateUpgrader{
			{
				Type:    resourceComputeResourceV1().CoreConfigSchema().ImpliedType(),
				Upgrade: resourceCompueteStateUpgradeV1,
				Version: 1,
			},
			{
				Type:    resourceComputeResourceV2().CoreConfigSchema().ImpliedType(),
				Upgrade: resourceComputeStateUpgradeV2,
				Version: 2,
			},
		},
	}
}
