package kvmvm import ( "context" "encoding/json" "sort" "strconv" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" log "github.com/sirupsen/logrus" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/compute" "repository.basistech.ru/BASIS/terraform-provider-decort/internal/flattens" ) func flattenCompute(ctx context.Context, d *schema.ResourceData, computeRec *compute.RecordCompute) error { log.Debugf("flattenCompute: ID %d, RG ID %d", computeRec.ID, computeRec.RGID) customFields, _ := json.Marshal(computeRec.CustomFields) devices, _ := json.Marshal(computeRec.Devices) userData, _ := json.Marshal(computeRec.Userdata) bootDisk := findBootDisk(computeRec.Disks) if len(computeRec.Interfaces) > 0 { log.Debugf("flattenCompute: calling parseComputeInterfacesToNetworks for %d interfaces", len(computeRec.Interfaces)) if err := d.Set("network", parseComputeInterfacesToNetworks(computeRec.Interfaces)); err != nil { return err } } d.Set("account_id", computeRec.AccountID) d.Set("account_name", computeRec.AccountName) d.Set("acl", flattenListACLInterface(computeRec.ACL)) d.Set("affinity_label", computeRec.AffinityLabel) d.Set("affinity_weight", computeRec.AffinityWeight) d.Set("affinity_rules", flattenAffinityRules(computeRec.AffinityRules)) d.Set("anti_affinity_rules", flattenAffinityRules(computeRec.AntiAffinityRules)) d.Set("arch", computeRec.Arch) d.Set("boot_order", computeRec.BootOrder) d.Set("boot_disk_id", bootDisk.ID) // we intentionally use the SizeMax field, do not change it until the BootDiskSize field is fixed on the platform d.Set("boot_disk_size", bootDisk.SizeMax) d.Set("cd_image_id", computeRec.CdImageId) d.Set("clone_reference", computeRec.CloneReference) d.Set("clones", computeRec.Clones) d.Set("computeci_id", computeRec.ComputeCIID) d.Set("created_by", computeRec.CreatedBy) d.Set("created_time", computeRec.CreatedTime) d.Set("custom_fields", string(customFields)) d.Set("deleted_by", computeRec.DeletedBy) d.Set("deleted_time", computeRec.DeletedTime) d.Set("description", computeRec.Description) d.Set("devices", string(devices)) err := d.Set("disks", flattenComputeDisks(ctx, d, computeRec.Disks, d.Get("extra_disks").(*schema.Set).List(), bootDisk.ID)) if err != nil { return err } d.Set("driver", computeRec.Driver) d.Set("gid", computeRec.GID) d.Set("guid", computeRec.GUID) d.Set("compute_id", computeRec.ID) d.Set("image_id", computeRec.ImageID) d.Set("interfaces", flattenInterfaces(computeRec.Interfaces)) d.Set("lock_status", computeRec.LockStatus) d.Set("manager_id", computeRec.ManagerID) d.Set("manager_type", computeRec.ManagerType) d.Set("migrationjob", computeRec.MigrationJob) d.Set("milestones", computeRec.Milestones) d.Set("need_reboot", computeRec.NeedReboot) d.Set("os_users", flattenOSUsers(computeRec.OSUsers)) d.Set("pinned", computeRec.Pinned) d.Set("reference_id", computeRec.ReferenceID) d.Set("registered", computeRec.Registered) d.Set("res_name", computeRec.ResName) d.Set("rg_name", computeRec.RGName) d.Set("snap_sets", flattenSnapSets(computeRec.SnapSets)) d.Set("stack_id", computeRec.StackID) d.Set("stack_name", computeRec.StackName) d.Set("stateless_sep_id", computeRec.StatelessSEPID) d.Set("stateless_sep_type", computeRec.StatelessSEPType) d.Set("status", computeRec.Status) d.Set("tags", flattenTags(computeRec.Tags)) d.Set("tech_status", computeRec.TechStatus) d.Set("updated_by", computeRec.UpdatedBy) d.Set("updated_time", computeRec.UpdatedTime) d.Set("user_data", string(userData)) d.Set("user_managed", computeRec.UserManaged) d.Set("vgpus", computeRec.VGPUs) d.Set("virtual_image_id", computeRec.VirtualImageID) return nil } func flattenTags(tags map[string]interface{}) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(tags)) for k, v := range tags { res = append(res, map[string]interface{}{ "key": k, "val": v, }) } return res } func flattenSnapSets(snaps compute.ListSnapshots) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(snaps)) for _, snap := range snaps { res = append(res, map[string]interface{}{ "disks": snap.Disks, "guid": snap.GUID, "label": snap.Label, "timestamp": snap.Timestamp, }) } return res } func flattenOSUsers(users compute.ListOSUsers) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(users)) for _, user := range users { res = append(res, map[string]interface{}{ "guid": user.GUID, "login": user.Login, "password": user.Password, "public_key": user.PubKey, }) } return res } func flattenInterfaces(ifaces compute.ListInterfaces) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(ifaces)) for _, iface := range ifaces { res = append(res, map[string]interface{}{ "conn_id": iface.ConnID, "conn_type": iface.ConnType, "def_gw": iface.DefGW, "flip_group_id": iface.FLIPGroupID, "guid": iface.GUID, "ip_address": iface.IPAddress, "listen_ssh": iface.ListenSSH, "mac": iface.MAC, "name": iface.Name, "net_id": iface.NetID, "netmask": iface.NetMask, "net_type": iface.NetType, "pci_slot": iface.PCISlot, "qos": flattenQOS(iface.QOS), "target": iface.Target, "type": iface.Type, "vnfs": iface.VNFs, }) } return res } func flattenQOS(qos compute.QOS) []map[string]interface{} { return []map[string]interface{}{ { "e_rate": qos.ERate, "guid": qos.GUID, "in_brust": qos.InBurst, "in_rate": qos.InRate, }, } } func flattenComputeDisks(ctx context.Context, d *schema.ResourceData, disksList compute.ListDisks, extraDisks []interface{}, bootDiskId uint64) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(disksList)) for _, disk := range disksList { if disk.ID == bootDiskId || findInExtraDisks(uint(disk.ID), extraDisks) { //skip main bootdisk and extraDisks continue } permanently, ok := ctx.Value(DiskKey(strconv.Itoa(int(disk.ID)))).(bool) // get permamently from Create or Update context if !ok { permanently = getPermanentlyByDiskID(d, disk.ID) // get permanently from state when Read is not after Create/Update } temp := map[string]interface{}{ "disk_name": disk.Name, "size": disk.SizeMax, "sep_id": disk.SEPID, "disk_type": disk.Type, "pool": disk.Pool, "desc": disk.Description, "image_id": disk.ImageID, "disk_id": disk.ID, "shareable": disk.Shareable, "size_used": disk.SizeUsed, "size_max": disk.SizeMax, "permanently": permanently, } res = append(res, temp) } sort.Slice(res, func(i, j int) bool { return res[i]["disk_id"].(uint64) < res[j]["disk_id"].(uint64) }) return res } // getPermanentlyByDiskID gets permanently value of specific disk (by diskId) from disks current state func getPermanentlyByDiskID(d *schema.ResourceData, diskId uint64) bool { disks := d.Get("disks").([]interface{}) for _, diskItem := range disks { disk := diskItem.(map[string]interface{}) if uint64(disk["disk_id"].(int)) == diskId { return disk["permanently"].(bool) } } log.Infof("getPermanentlyByDiskID: disk with id %d not found in state", diskId) return false } func findInExtraDisks(diskId uint, extraDisks []interface{}) bool { for _, ExtraDisk := range extraDisks { if diskId == uint(ExtraDisk.(int)) { return true } } return false } func flattenAffinityRules(rules compute.ListRules) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(rules)) for _, rule := range rules { res = append(res, map[string]interface{}{ "topology": rule.Topology, "policy": rule.Policy, "mode": rule.Mode, "key": rule.Key, "value": rule.Value, }) } return res } func flattenComputeList(computes *compute.ListComputes) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(computes.Data)) for _, computeItem := range computes.Data { customFields, _ := json.Marshal(computeItem.CustomFields) devices, _ := json.Marshal(computeItem.Devices) userData, _ := json.Marshal(computeItem.Userdata) temp := map[string]interface{}{ "acl": flattenListACLInterface(computeItem.ACL), "account_id": computeItem.AccountID, "account_name": computeItem.AccountName, "affinity_label": computeItem.AffinityLabel, "affinity_rules": flattenListRules(computeItem.AffinityRules), "affinity_weight": computeItem.AffinityWeight, "anti_affinity_rules": flattenListRules(computeItem.AntiAffinityRules), "arch": computeItem.Arch, "cd_image_id": computeItem.CdImageId, "boot_order": computeItem.BootOrder, "boot_disk_size": computeItem.BootDiskSize, "clone_reference": computeItem.CloneReference, "clones": computeItem.Clones, "computeci_id": computeItem.ComputeCIID, "cpus": computeItem.CPUs, "created_by": computeItem.CreatedBy, "created_time": computeItem.CreatedTime, "custom_fields": string(customFields), "deleted_by": computeItem.DeletedBy, "deleted_time": computeItem.DeletedTime, "desc": computeItem.Description, "devices": string(devices), "disks": flattenDisks(computeItem.Disks), "driver": computeItem.Driver, "gid": computeItem.GID, "guid": computeItem.GUID, "compute_id": computeItem.ID, "image_id": computeItem.ImageID, "interfaces": flattenInterfaces(computeItem.Interfaces), "lock_status": computeItem.LockStatus, "manager_id": computeItem.ManagerID, "manager_type": computeItem.ManagerType, "migrationjob": computeItem.MigrationJob, "milestones": computeItem.Milestones, "name": computeItem.Name, "need_reboot": computeItem.NeedReboot, "os_users": flattenOSUsers(computeItem.OSUsers), "pinned": computeItem.Pinned, "ram": computeItem.RAM, "reference_id": computeItem.ReferenceID, "registered": computeItem.Registered, "res_name": computeItem.ResName, "rg_id": computeItem.RGID, "rg_name": computeItem.RGName, "snap_sets": flattenSnapSets(computeItem.SnapSets), "stack_id": computeItem.StackID, "stateless_sep_id": computeItem.StatelessSEPID, "stateless_sep_type": computeItem.StatelessSEPType, "status": computeItem.Status, "tags": flattenTags(computeItem.Tags), "tech_status": computeItem.TechStatus, "total_disk_size": computeItem.TotalDiskSize, "updated_by": computeItem.UpdatedBy, "updated_time": computeItem.UpdatedTime, "user_data": string(userData), "user_managed": computeItem.UserManaged, "vgpus": computeItem.VGPUs, "vins_connected": computeItem.VINSConnected, "virtual_image_id": computeItem.VirtualImageID, } res = append(res, temp) } return res } func flattenListACLInterface(listAcl []interface{}) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(listAcl)) for _, aclInterface := range listAcl { acl := aclInterface.(map[string]interface{}) temp := map[string]interface{}{ "explicit": acl["explicit"], "guid": acl["guid"], "right": acl["right"], "status": acl["status"], "type": acl["type"], "user_group_id": acl["user_group_id"], } res = append(res, temp) } return res } func flattenListACL(listAcl compute.ListACL) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(listAcl)) for _, acl := range listAcl { temp := map[string]interface{}{ "explicit": acl.Explicit, "guid": acl.GUID, "right": acl.Right, "status": acl.Status, "type": acl.Type, "user_group_id": acl.UserGroupID, } res = append(res, temp) } return res } func flattenListComputeACL(listAcl []compute.ItemComputeACL) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(listAcl)) for _, acl := range listAcl { temp := map[string]interface{}{ "explicit": acl.Explicit, "guid": acl.GUID, "right": acl.Right, "status": acl.Status, "type": acl.Type, "user_group_id": acl.UserGroupID, } res = append(res, temp) } return res } func flattenListRules(listRules compute.ListRules) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(listRules)) for _, rule := range listRules { temp := map[string]interface{}{ "guid": rule.GUID, "key": rule.Key, "mode": rule.Mode, "policy": rule.Policy, "topology": rule.Topology, "value": rule.Value, } res = append(res, temp) } return res } func flattenDisks(disks []compute.InfoDisk) []map[string]interface{} { res := make([]map[string]interface{}, 0) for _, disk := range disks { temp := map[string]interface{}{ "disk_id": disk.ID, "pci_slot": disk.PCISlot, } res = append(res, temp) } return res } func flattenComputeAudits(computeAudits compute.ListDetailedAudits) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(computeAudits)) for _, computeAudit := range computeAudits { temp := map[string]interface{}{ "call": computeAudit.Call, "responsetime": computeAudit.ResponseTime, "statuscode": computeAudit.StatusCode, "timestamp": computeAudit.Timestamp, "user": computeAudit.User, } res = append(res, temp) } return res } func flattenComputeGetAudits(computeAudits compute.ListAudits) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(computeAudits)) for _, computeAudit := range computeAudits { temp := map[string]interface{}{ "epoch": computeAudit.Epoch, "message": computeAudit.Message, } res = append(res, temp) } return res } func flattenPfwList(computePfws *compute.ListPFW) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(computePfws.Data)) for _, computePfw := range computePfws.Data { temp := map[string]interface{}{ "pfw_id": computePfw.ID, "local_ip": computePfw.LocalIP, "local_port": computePfw.LocalPort, "protocol": computePfw.Protocol, "public_port_end": computePfw.PublicPortEnd, "public_port_start": computePfw.PublicPortStart, "vm_id": computePfw.VMID, } res = append(res, temp) } return res } func flattenUserList(d *schema.ResourceData, userList *compute.ListUsers) { d.Set("account_acl", flattenListACL(userList.Data.AccountACL)) d.Set("compute_acl", flattenListComputeACL(userList.Data.ComputeACL)) d.Set("rg_acl", flattenListACL(userList.Data.RGACL)) } func flattenSnapshotList(computeSnapshots *compute.ListSnapShot) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(computeSnapshots.Data)) for _, snp := range computeSnapshots.Data { temp := map[string]interface{}{ "disks": snp.Disks, "guid": snp.GUID, "label": snp.Label, "timestamp": snp.Timestamp, } res = append(res, temp) } return res } func flattenAffinityRelations(d *schema.ResourceData, ar *compute.RecordAffinityRelations) { d.Set("other_node", flattenNodes(ar.OtherNode)) d.Set("other_node_indirect", flattenNodes(ar.OtherNodeIndirect)) d.Set("other_node_indirect_soft", flattenNodes(ar.OtherNodeIndirectSoft)) d.Set("other_node_soft", flattenNodes(ar.OtherNodeSoft)) d.Set("same_node", flattenNodes(ar.SameNode)) d.Set("same_node_soft", flattenNodes(ar.SameNodeSoft)) } func flattenSnapshotUsage(computeSnapshotUsages compute.ListSnapshotUsage) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(computeSnapshotUsages)) for _, computeUsage := range computeSnapshotUsages { temp := map[string]interface{}{ "count": computeUsage.Count, "stored": computeUsage.Stored, "label": computeUsage.Label, "timestamp": computeUsage.Timestamp, } res = append(res, temp) } return res } func flattenPCIDevice(deviceList []compute.ItemPCIDevice) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(deviceList)) for _, dev := range deviceList { temp := map[string]interface{}{ "ckey": dev.CKey, "meta": flattens.FlattenMeta(dev.Meta), "compute_id": dev.ComputeID, "description": dev.Description, "guid": dev.GUID, "hwpath": dev.HwPath, "device_id": dev.ID, "name": dev.Name, "rg_id": dev.RGID, "stack_id": dev.StackID, "status": dev.Status, "system_name": dev.SystemName, } res = append(res, temp) } return res } func flattenVGPU(m []interface{}) []string { var output []string for _, item := range m { switch d := item.(type) { case string: output = append(output, d) case int: output = append(output, strconv.Itoa(d)) case int64: output = append(output, strconv.FormatInt(d, 10)) case float64: output = append(output, strconv.FormatInt(int64(d), 10)) default: output = append(output, "") } } return output } func flattenNodes(m []interface{}) []string { var output []string for _, item := range m { switch d := item.(type) { case string: output = append(output, d) case int: output = append(output, strconv.Itoa(d)) case int64: output = append(output, strconv.FormatInt(d, 10)) case float64: output = append(output, strconv.FormatInt(int64(d), 10)) default: output = append(output, "") } } return output } func flattenDataCompute(d *schema.ResourceData, compFacts *compute.RecordCompute) error { // This function expects that compFacts string contains response from API compute/get, // i.e. detailed information about compute instance. // // NOTE: this function modifies ResourceData argument - as such it should never be called // from resourceComputeExists(...) method log.Debugf("flattenCompute: ID %d, RG ID %d", compFacts.ID, compFacts.RGID) customFields, _ := json.Marshal(compFacts.CustomFields) devices, _ := json.Marshal(compFacts.Devices) userData, _ := json.Marshal(compFacts.Userdata) d.Set("account_id", compFacts.AccountID) d.Set("account_name", compFacts.AccountName) d.Set("acl", flattenListACLInterface(compFacts.ACL)) d.Set("affinity_label", compFacts.AffinityLabel) d.Set("affinity_rules", flattenAffinityRules(compFacts.AffinityRules)) d.Set("affinity_weight", compFacts.AffinityWeight) d.Set("anti_affinity_rules", flattenAffinityRules(compFacts.AntiAffinityRules)) d.Set("arch", compFacts.Arch) d.Set("boot_order", compFacts.BootOrder) d.Set("boot_disk_size", compFacts.BootDiskSize) d.Set("cd_image_id", compFacts.CdImageId) d.Set("clone_reference", compFacts.CloneReference) d.Set("clones", compFacts.Clones) d.Set("computeci_id", compFacts.ComputeCIID) d.Set("cpus", compFacts.CPUs) d.Set("created_by", compFacts.CreatedBy) d.Set("created_time", compFacts.CreatedTime) d.Set("custom_fields", string(customFields)) d.Set("deleted_by", compFacts.DeletedBy) d.Set("deleted_time", compFacts.DeletedTime) d.Set("desc", compFacts.Description) d.Set("devices", string(devices)) d.Set("disks", flattenDisk(compFacts.Disks)) d.Set("driver", compFacts.Driver) d.Set("gid", compFacts.GID) d.Set("guid", compFacts.GUID) d.Set("image_id", compFacts.ImageID) d.Set("interfaces", flattenInterfaces(compFacts.Interfaces)) d.Set("lock_status", compFacts.LockStatus) d.Set("manager_id", compFacts.ManagerID) d.Set("manager_type", compFacts.ManagerType) d.Set("migrationjob", compFacts.MigrationJob) d.Set("milestones", compFacts.Milestones) d.Set("name", compFacts.Name) d.Set("need_reboot", compFacts.NeedReboot) d.Set("os_users", flattenOSUsers(compFacts.OSUsers)) d.Set("pinned", compFacts.Pinned) d.Set("ram", compFacts.RAM) d.Set("reference_id", compFacts.ReferenceID) d.Set("registered", compFacts.Registered) d.Set("res_name", compFacts.ResName) d.Set("rg_id", compFacts.RGID) d.Set("rg_name", compFacts.RGName) d.Set("snap_sets", flattenSnapSets(compFacts.SnapSets)) d.Set("stack_id", compFacts.StackID) d.Set("stack_name", compFacts.StackName) d.Set("stateless_sep_id", compFacts.StatelessSEPID) d.Set("stateless_sep_type", compFacts.StatelessSEPType) d.Set("status", compFacts.Status) d.Set("tags", flattenTags(compFacts.Tags)) d.Set("tech_status", compFacts.TechStatus) d.Set("updated_by", compFacts.UpdatedBy) d.Set("updated_time", compFacts.UpdatedTime) d.Set("user_data", string(userData)) d.Set("user_managed", compFacts.UserManaged) d.Set("vgpus", compFacts.VGPUs) d.Set("virtual_image_id", compFacts.VirtualImageID) return nil } // Parse the list of interfaces from compute/get response into a list of networks // attached to this compute func parseComputeInterfacesToNetworks(ifaces compute.ListInterfaces) []interface{} { // return value will be used to d.Set("network") item of dataSourceCompute schema length := len(ifaces) log.Debugf("parseComputeInterfacesToNetworks: called for %d ifaces", length) result := []interface{}{} for _, value := range ifaces { elem := make(map[string]interface{}) // Keys in this map should correspond to the Schema definition for "network" elem["net_id"] = value.NetID elem["net_type"] = value.NetType elem["ip_address"] = value.IPAddress elem["mac"] = value.MAC result = append(result, elem) } return result } func flattenDisk(diskList compute.ListDisks) []map[string]interface{} { res := make([]map[string]interface{}, 0, len(diskList)) for _, disk := range diskList { temp := map[string]interface{}{ "ckey": disk.CKey, "meta": flattens.FlattenMeta(disk.Meta), "account_id": disk.AccountID, "boot_partition": disk.BootPartition, "created_time": disk.CreatedTime, "deleted_time": disk.DeletedTime, "desc": disk.Description, "destruction_time": disk.DestructionTime, "disk_path": disk.DiskPath, "gid": disk.GID, "guid": disk.GUID, "disk_id": disk.ID, "image_id": disk.ImageID, "images": disk.Images, "iotune": flattenIOTune(disk.IOTune), "iqn": disk.IQN, "login": disk.Login, "milestones": disk.Milestones, "name": disk.Name, "order": disk.Order, "params": disk.Params, "parent_id": disk.ParentID, "passwd": disk.Password, "pci_slot": disk.PCISlot, "pool": disk.Pool, "purge_attempts": disk.PurgeAttempts, "present_to": disk.PresentTo, "purge_time": disk.PurgeTime, "reality_device_number": disk.RealityDeviceNumber, "reference_id": disk.ReferenceID, "res_id": disk.ResID, "res_name": disk.ResName, "role": disk.Role, "sep_id": disk.SEPID, "shareable": disk.Shareable, "size_max": disk.SizeMax, "size_used": disk.SizeUsed, "snapshots": flattendDiskSnapshotList(disk.Snapshots), "status": disk.Status, "tech_status": disk.TechStatus, "type": disk.Type, "vmid": disk.VMID, } res = append(res, temp) } return res } func flattenIOTune(iot compute.IOTune) []map[string]interface{} { res := make([]map[string]interface{}, 0) temp := map[string]interface{}{ "read_bytes_sec": iot.ReadBytesSec, "read_bytes_sec_max": iot.ReadBytesSecMax, "read_iops_sec": iot.ReadIOPSSec, "read_iops_sec_max": iot.ReadIOPSSecMax, "size_iops_sec": iot.SizeIOPSSec, "total_bytes_sec": iot.TotalBytesSec, "total_bytes_sec_max": iot.TotalBytesSecMax, "total_iops_sec": iot.TotalIOPSSec, "total_iops_sec_max": iot.TotalIOPSSecMax, "write_bytes_sec": iot.WriteBytesSec, "write_bytes_sec_max": iot.WriteBytesSecMax, "write_iops_sec": iot.WriteIOPSSec, "write_iops_sec_max": iot.WriteIOPSSecMax, } res = append(res, temp) return res } func flattendDiskSnapshotList(sl compute.ListDetailedSnapshots) []interface{} { res := make([]interface{}, 0) for _, snapshot := range sl { temp := map[string]interface{}{ "guid": snapshot.GUID, "label": snapshot.Label, "res_id": snapshot.ResID, "snap_set_guid": snapshot.SnapSetGUID, "snap_set_time": snapshot.SnapSetTime, "timestamp": snapshot.TimeStamp, } res = append(res, temp) } return res }