/*
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"
	"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))
	}

	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 disks, ok := d.GetOk("disks"); ok {
		if isMoreThanOneDisksTypeB(ctx, disks) {
			return diag.Errorf("resourceComputeCreate: can't create Compute because block disks have more 1 disk type 'B'")
		}
	}

	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)
				}
			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 ipaType, ok := d.GetOk("ipa_type"); ok {
		createReqX86.IPAType = ipaType.(string)
	}

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

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

	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 reqInterface.NetType == "DPDK" {
					reqInterface.MTU = uint64(netInterfaceVal["mtu"].(int))
				}

				ipaddr, ipSet := netInterfaceVal["ip_address"]
				if ipSet {
					reqInterface.IPAddr = ipaddr.(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)),
			}
			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
	driver := d.Get("driver").(string)

	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.Driver = driver

	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)

	log.Debugf("resourceComputeCreate: creating Compute of type KVM VM x86")
	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{}

	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))

	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 !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)
				}
			}
		}

		// 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}
				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 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_stack").(bool) {
			req := compute.PinToStackRequest{
				ComputeID: computeId,
			}
			_, err := c.CloudAPI().Compute().PinToStack(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)
			}
		}

		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)
					}
				}
			}
		}
	}
	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 bacause rgID %d not allowed or does not exist", d.Get("rg_id").(int))
	}

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

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

	if disks, ok := d.GetOk("disks"); ok {
		if isMoreThanOneDisksTypeB(ctx, disks) {
			return diag.Errorf("resourceComputeUpdate: can't update Compute because block disks have more 1 disk type 'B'")
		}
	}

	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)
				}
			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 _, 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)
	}

	if d.HasChange("started") {
		start := d.Get("started").(bool)
		if start {
			req := compute.StartRequest{ComputeID: computeRec.ID}

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

			if _, err := c.CloudAPI().Compute().Stop(ctx, req); 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
	}

	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)
		}
	}

	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)
		}
	}

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

	if d.HasChanges("description", "name", "numa_affinity", "cpu_pin", "hp_backed") {
		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("cpu_pin") {
			req.CPUPin = d.Get("cpu_pin").(bool)
		}
		if d.HasChange("hp_backed") {
			req.HPBacked = d.Get("hp_backed").(bool)
		}

		if d.HasChange("chipset") {
			req.Chipset = d.Get("chipset").(string)
		}

		// Note bene: numa_affinity, cpu_pin and hp_backed are not allowed to be changed for compute in STARTED tech status.
		// If STARTED, we need to stop it before update
		var isStopRequired bool
		if d.HasChanges("numa_affinity", "cpu_pin", "hp_backed") && d.Get("started").(bool) {
			isStopRequired = true
		}
		if isStopRequired {
			if _, err := c.CloudAPI().Compute().Stop(ctx, compute.StopRequest{ComputeID: computeRec.ID}); err != nil {
				return diag.FromErr(err)
			}
		}

		// 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 {
			if _, err := c.CloudAPI().Compute().Start(ctx, compute.StartRequest{ComputeID: computeRec.ID}); 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)

		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) {
				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 len(deletedDisks) > 0 {
			stopReq := compute.StopRequest{
				ComputeID: computeRec.ID,
				Force:     false,
			}

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

			for _, disk := range deletedDisks {
				diskConv := disk.(map[string]interface{})
				if diskConv["disk_type"].(string) == "B" {
					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)
				}
			}
			req := compute.StartRequest{
				ComputeID: computeRec.ID,
				AltBootID: 0,
			}
			_, err = c.CloudAPI().Compute().Start(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}

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

				if diskConv["sep_id"].(int) != 0 {
					req.SepID = uint64(diskConv["sep_id"].(int))
				}
				if diskConv["disk_type"].(string) != "" {
					req.DiskType = diskConv["disk_type"].(string)
				}
				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))
				}
				_, err := c.CloudAPI().Compute().DiskAdd(ctx, req)
				if err != nil {
					return diag.FromErr(err)
				}
			}
		}

		if len(resizedDisks) > 0 {
			for _, disk := range resizedDisks {
				diskConv := disk.(map[string]interface{})
				if diskConv["disk_type"].(string) == "B" {
					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 d.HasChange("started") {
		if d.Get("started").(bool) {
			req := compute.StartRequest{
				ComputeID: computeRec.ID,
			}
			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("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("pin_to_stack") {
		oldPin, newPin := d.GetChange("pin_to_stack")
		if !newPin.(bool) {
			req := compute.UnpinFromStackRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().UnpinFromStack(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
		if !oldPin.(bool) {
			req := compute.PinToStackRequest{
				ComputeID: computeRec.ID,
			}

			_, err := c.CloudAPI().Compute().PinToStack(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,
		}

		_, 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,
				ImageID:   uint64(newImage.(int)),
			}

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

			_, 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)
		}
	}

	// 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 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)

	pciList, ok := d.GetOk("pci_devices")

	if d.Get("permanently").(bool) && ok {
		pciDevices := pciList.(*schema.Set).List()
		for _, v := range pciDevices {
			pciID := v.(int)
			req := compute.DetachPCIDeviceRequest{
				ComputeID: computeId,
				DeviceID:  uint64(pciID),
			}
			_, err := c.CloudAPI().Compute().DetachPCIDevice(ctx, req)
			if err != nil {
				return diag.FromErr(err)
			}
		}
	}
	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",
		},
		"sep_id": {
			Type:        schema.TypeInt,
			Computed:    true,
			Optional:    true,
			Description: "Storage endpoint provider ID; by default the same with boot disk",
		},
		"disk_type": {
			Type:         schema.TypeString,
			Computed:     true,
			Optional:     true,
			ValidateFunc: validation.StringInSlice([]string{"B", "D"}, false),
			Description:  "The type of disk in terms of its role in compute: 'B=Boot, D=Data'",
		},
		"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",
		},
		"disk_id": {
			Type:        schema.TypeInt,
			Computed:    true,
			Description: "Disk ID",
		},
		"shareable": {
			Type:     schema.TypeBool,
			Computed: true,
		},
		"size_max": {
			Type:     schema.TypeInt,
			Computed: true,
		},
		"size_used": {
			Type:     schema.TypeInt,
			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.",
		},
		"driver": {
			Type:     schema.TypeString,
			Required: true,
			// ForceNew:     true,
			StateFunc:    statefuncs.StateFuncToUpper,
			ValidateFunc: validation.StringInSlice([]string{"SVA_KVM_X86", "KVM_X86"}, false), // observe case while validating
			Description:  "Hardware architecture of this compute instance.",
		},
		"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.",
		},
		"image_id": {
			Type:     schema.TypeInt,
			Optional: true,
			//ForceNew:    true, //REDEPLOY
			Description: "ID of the OS image to base this compute instance on.",
		},
		"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.",
		},
		"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,
						Optional:    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,
						Optional:    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,
		},
		"stateless": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "Compute will be stateless (SVA_KVM_X86) if set to True",
		},
		"boot_disk": {
			Type:     schema.TypeSet,
			Computed: true,
			Elem: &schema.Resource{
				Schema: disksSubresourceSchemaMake(),
			},
		},
		"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,
			MaxItems: constants.MAX_NETWORKS_PER_COMPUTE,
			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_stack": {
			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",
		},
		"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": {
			Type:        schema.TypeBool,
			Optional:    true,
			Default:     false,
			Description: "Flag for redeploy compute",
		},
		"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",
		},
		"data_disks": {
			Type:         schema.TypeString,
			Optional:     true,
			ValidateFunc: validation.StringInSlice([]string{"KEEP", "DETACH", "DESTROY"}, false),
			Default:      "DETACH",
			Description:  "Flag for redeploy compute",
		},
		"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,
		},
		"is": {
			Type:        schema.TypeString,
			Optional:    true,
			Description: "system name",
		},
		"ipa_type": {
			Type:        schema.TypeString,
			Optional:    true,
			Description: "compute purpose",
		},
		"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.",
		},
		"pci_devices": {
			Type:     schema.TypeSet,
			Optional: true,
			Computed: true,
			Elem: &schema.Schema{
				Type: schema.TypeInt,
			},
			Description: "ID of the connected pci devices",
		},

		// 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,
		},
		"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,
		},
		"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,
		},
		"vgpus": {
			Type:     schema.TypeList,
			Computed: true,
			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,
		},
	}
	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("network", "affinity_rules", "anti_affinity_rules",
				"disks", "extra_disks", "tags", "port_forwarding", "user_access", "snapshot", "pci_devices") {
				diff.SetNewComputed("updated_time")
				diff.SetNewComputed("updated_by")
			}
			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,
			},
		},
	}
}
