package flattens import ( "context" "encoding/json" "fmt" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/flattens" disks "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/disks/models" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/kvmvm/models" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/kvmvm/utilities" ) func ComputeResource(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics { tflog.Info(ctx, "Start flattens.ComputeResource") diags := diag.Diagnostics{} recordItemCompute, diags := utilities.ComputeResourceCheckPresence(ctx, plan, c) if diags.HasError() { return diags } bootdisk := findBootDisk(recordItemCompute.Disks) devices, _ := json.Marshal(recordItemCompute.Devices) userdata, _ := json.Marshal(recordItemCompute.Userdata) customFields, _ := json.Marshal(recordItemCompute.CustomFields) *plan = models.ResourceComputeModel{ // required fields Name: plan.Name, RGID: plan.RGID, Driver: plan.Driver, CPU: plan.CPU, RAM: plan.RAM, ID: plan.ID, Timeouts: plan.Timeouts, // optional fields ImageID: plan.ImageID, WithoutBootDisk: plan.WithoutBootDisk, // we intentionally use the SizeMax field, do not change it until the BootDiskSize field is fixed on the platform BootDiskSize: types.Int64Value(int64(bootdisk.SizeMax)), AffinityLabel: types.StringValue(recordItemCompute.AffinityLabel), AffinityRules: plan.AffinityRules, AntiAffinityRules: plan.AntiAffinityRules, AutoStartWithNode: types.BoolValue(recordItemCompute.AutoStart), CustomFields: types.StringValue(string(customFields)), Chipset: types.StringValue(recordItemCompute.Chipset), Stateless: plan.Stateless, SepId: types.Int64Value(int64(bootdisk.SepID)), Pool: types.StringValue(bootdisk.Pool), ExtraDisks: plan.ExtraDisks, Network: flattenNetwork(ctx, plan.Network, &recordItemCompute.Interfaces), Tags: plan.Tags, PortForwarding: plan.PortForwarding, UserAccess: plan.UserAccess, Snapshot: plan.Snapshot, PCIDevices: plan.PCIDevices, Rollback: plan.Rollback, CD: plan.CD, PinToStack: plan.PinToStack, Description: types.StringValue(recordItemCompute.Description), CloudInit: plan.CloudInit, Enabled: plan.Enabled, Pause: plan.Pause, Reset: plan.Reset, Restore: plan.Restore, AutoStart: plan.AutoStart, ForceStop: plan.ForceStop, ForceResize: plan.ForceResize, DataDisks: plan.DataDisks, Started: plan.Started, DetachDisks: plan.DetachDisks, Permanently: plan.Permanently, IS: plan.IS, IpaType: plan.IpaType, NumaAffinity: plan.NumaAffinity, CPUPin: plan.CPUPin, HPBacked: plan.HPBacked, //computed fields AccountId: types.Int64Value(int64(recordItemCompute.AccountID)), AccountName: types.StringValue(recordItemCompute.AccountName), ACL: flattenResourceACL(ctx, &recordItemCompute.ACL), AffinityWeight: types.Int64Value(int64(recordItemCompute.AffinityWeight)), Architecture: types.StringValue(recordItemCompute.Architecture), BootOrder: flattens.FlattenSimpleTypeToList(ctx, types.StringType, recordItemCompute.BootOrder), BootDisk: flattenDisk(ctx, bootdisk), BootDiskId: types.Int64Value(int64(bootdisk.ID)), CdImageId: types.Int64Value(int64(recordItemCompute.CdImageId)), CloneReference: types.Int64Value(int64(recordItemCompute.CloneReference)), Clones: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordItemCompute.Clones), ComputeCIID: types.Int64Value(int64(recordItemCompute.ComputeCIID)), ComputeId: types.Int64Value(int64(recordItemCompute.ID)), CreatedBy: types.StringValue(recordItemCompute.CreatedBy), CreatedTime: types.Int64Value(int64(recordItemCompute.CreatedTime)), DeletedBy: types.StringValue(recordItemCompute.DeletedBy), DeletedTime: types.Int64Value(int64(recordItemCompute.DeletedTime)), Devices: types.StringValue(string(devices)), Disks: flattenResourceDisks(ctx, &recordItemCompute.Disks), GID: types.Int64Value(int64(recordItemCompute.GID)), GUID: types.Int64Value(int64(recordItemCompute.GUID)), ImageName: types.StringValue(recordItemCompute.ImageName), Interfaces: flattenResourceInterfaces(ctx, &recordItemCompute.Interfaces), LockStatus: types.StringValue(recordItemCompute.LockStatus), ManagerID: types.Int64Value(int64(recordItemCompute.ManagerID)), ManagerType: types.StringValue(recordItemCompute.ManagerType), MigrationJob: types.Int64Value(int64(recordItemCompute.MigrationJob)), Milestones: types.Int64Value(int64(recordItemCompute.Milestones)), NatableVINSID: types.Int64Value(int64(recordItemCompute.NatableVINSID)), NatableVINSIP: types.StringValue(recordItemCompute.NatableVINSIP), NatableVINSName: types.StringValue(recordItemCompute.NatableVINSName), NatableVINSNetwork: types.StringValue(recordItemCompute.NatableVINSNetwork), NatableVINSNetworkName: types.StringValue(recordItemCompute.NatableVINSNetworkName), NeedReboot: types.BoolValue(recordItemCompute.NeedReboot), NumaNodeId: types.Int64Value(int64(recordItemCompute.NumaNodeId)), OSUsers: flattenResourceOSUsers(ctx, &recordItemCompute.OSUsers), Pinned: types.BoolValue(recordItemCompute.Pinned), PreferredCPU: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordItemCompute.PreferredCPU), ReferenceID: types.StringValue(recordItemCompute.ReferenceID), Registered: types.BoolValue(recordItemCompute.Registered), ResName: types.StringValue(recordItemCompute.ResName), ReservedNodeCpus: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordItemCompute.ReservedNodeCpus), RGName: types.StringValue(recordItemCompute.RGName), SnapSets: flattenSnapSets(ctx, &recordItemCompute.SnapSets), StatelessSepID: types.Int64Value(int64(recordItemCompute.StatelessSepID)), StatelessSepType: types.StringValue(recordItemCompute.StatelessSepType), Status: types.StringValue(recordItemCompute.Status), TechStatus: types.StringValue(recordItemCompute.TechStatus), UpdatedBy: types.StringValue(recordItemCompute.UpdatedBy), UpdatedTime: types.Int64Value(int64(recordItemCompute.UpdatedTime)), UserManaged: types.BoolValue(recordItemCompute.UserManaged), Userdata: types.StringValue(string(userdata)), VGPUs: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordItemCompute.VGPUs), VirtualImageID: types.Int64Value(int64(recordItemCompute.VirtualImageID)), VirtualImageName: types.StringValue(recordItemCompute.VirtualImageName), VNCPassword: types.StringValue(recordItemCompute.VNCPassword), } tflog.Info(ctx, "End flattens.ComputeResource", map[string]any{"id": plan.ID.ValueString()}) return nil } func findBootDisk(disks compute.ListComputeDisks) *compute.ItemComputeDisk { for _, disk := range disks { if disk.Type == "B" { return &disk } } return &compute.ItemComputeDisk{} } func flattenResourceACL(ctx context.Context, acl *compute.RecordACL) types.Object { tflog.Info(ctx, "Start flattenACL") temp := models.RecordResourceACLModel{ AccountACL: flattenACLItems(ctx, &acl.AccountACL), ComputeACL: flattenACLItems(ctx, &acl.ComputeACL), RGACL: flattenACLItems(ctx, &acl.RGACL), } res, err := types.ObjectValueFrom(ctx, models.ListACL, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenACL struct to obj", err)) } tflog.Info(ctx, "End flattenACL") return res } func flattenACLItems(ctx context.Context, item *compute.ListACL) types.List { tflog.Info(ctx, "Start flattenACLItems") tempSlice := make([]types.Object, 0, len(*item)) for _, aclItem := range *item { temp := models.ItemACLModel{ Explicit: types.BoolValue(bool(aclItem.Explicit)), GUID: types.StringValue(aclItem.GUID), Right: types.StringValue(aclItem.Right), Status: types.StringValue(aclItem.Status), Type: types.StringValue(aclItem.Type), UserGroupID: types.StringValue(aclItem.UserGroupID), } obj, err := types.ObjectValueFrom(ctx, models.ItemACL, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenACLItems struct to obj", err)) } tempSlice = append(tempSlice, obj) } res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemACL}, tempSlice) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenACLItems", err)) } tflog.Info(ctx, "End flattenACLItems") return res } func flattenDisk(ctx context.Context, disk *compute.ItemComputeDisk) types.Object { tflog.Info(ctx, fmt.Sprintf("flattenDisk: start flatten disk with ID - %v", disk.ID)) acl, _ := json.Marshal(disk.ACL) temp := models.ItemResourceDiskModel{ CKey: types.StringValue(disk.CKey), ACL: types.StringValue(string(acl)), AccountID: types.Int64Value(int64(disk.AccountID)), BootPartition: types.Int64Value(int64(disk.BootPartition)), BusNumber: types.Int64Value(int64(disk.BusNumber)), CreatedTime: types.Int64Value(int64(disk.CreatedTime)), DeletedTime: types.Int64Value(int64(disk.DeletedTime)), Description: types.StringValue(disk.Description), DestructionTime: types.Int64Value(int64(disk.DestructionTime)), DiskPath: types.StringValue(disk.DiskPath), GID: types.Int64Value(int64(disk.GID)), GUID: types.Int64Value(int64(disk.GUID)), ID: types.Int64Value(int64(disk.ID)), ImageID: types.Int64Value(int64(disk.ImageID)), Images: flattens.FlattenSimpleTypeToList(ctx, types.StringType, disk.Images), IOTune: flattensIOTune(ctx, &disk.IOTune), IQN: types.StringValue(disk.IQN), Login: types.StringValue(disk.Login), Milestones: types.Int64Value(int64(disk.Milestones)), Name: types.StringValue(disk.Name), Order: types.Int64Value(int64(disk.Order)), Params: types.StringValue(disk.Params), ParentID: types.Int64Value(int64(disk.ParentID)), Passwd: types.StringValue(disk.Passwd), Pool: types.StringValue(disk.Pool), PCISlot: types.Int64Value(disk.PCISlot), PresentTo: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, disk.PresentTo), PurgeTime: types.Int64Value(int64(disk.PurgeTime)), RealityDeviceNumber: types.Int64Value(int64(disk.RealityDeviceNumber)), Replication: flattenDiskReplication(ctx, &disk.Replication), ResID: types.StringValue(disk.ResID), Role: types.StringValue(disk.Role), SepID: types.Int64Value(int64(disk.SepID)), Shareable: types.BoolValue(disk.Shareable), SizeMax: types.Int64Value(int64(disk.SizeMax)), SizeUsed: types.Float64Value(disk.SizeUsed), Snapshots: flattenResourceSnapshotExtend(ctx, &disk.Snapshots), Status: types.StringValue(disk.Status), TechStatus: types.StringValue(disk.TechStatus), Type: types.StringValue(disk.Type), VMID: types.Int64Value(int64(disk.VMID)), } res, err := types.ObjectValueFrom(ctx, models.ItemDisk, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenDisk struct to obj", err)) } tflog.Info(ctx, fmt.Sprintf("flattenDisk: end flatten disk with ID - %v", disk.ID)) return res } func flattensIOTune(ctx context.Context, ioTune *compute.IOTune) types.Object { tflog.Info(ctx, "Start flattensIOTune") temp := models.IOTuneModel{ ReadBytesSec: types.Int64Value(int64(ioTune.ReadBytesSec)), ReadBytesSecMax: types.Int64Value(int64(ioTune.ReadBytesSecMax)), ReadIOPSSec: types.Int64Value(int64(ioTune.ReadIOPSSec)), ReadIOPSSecMax: types.Int64Value(int64(ioTune.ReadIOPSSecMax)), SizeIOPSSec: types.Int64Value(int64(ioTune.SizeIOPSSec)), TotalBytesSec: types.Int64Value(int64(ioTune.TotalBytesSec)), TotalBytesSecMax: types.Int64Value(int64(ioTune.TotalBytesSecMax)), TotalIOPSSec: types.Int64Value(int64(ioTune.TotalIOPSSec)), TotalIOPSSecMax: types.Int64Value(int64(ioTune.TotalIOPSSecMax)), WriteBytesSec: types.Int64Value(int64(ioTune.WriteBytesSec)), WriteBytesSecMax: types.Int64Value(int64(ioTune.WriteBytesSecMax)), WriteIOPSSec: types.Int64Value(int64(ioTune.WriteIOPSSec)), WriteIOPSSecMax: types.Int64Value(int64(ioTune.WriteIOPSSecMax)), } res, err := types.ObjectValueFrom(ctx, disks.ItemIOTune, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattensIOTune struct to obj", err)) } tflog.Info(ctx, "End flattensIOTune") return res } func flattenDiskReplication(ctx context.Context, replication *compute.ItemReplication) types.Object { tflog.Info(ctx, "Start flattenDiskReplication") temp := models.ReplicationModel{ DiskID: types.Int64Value(int64(replication.DiskID)), PoolID: types.StringValue(replication.PoolID), Role: types.StringValue(replication.Role), SelfVolumeID: types.StringValue(replication.SelfVolumeID), StorageID: types.StringValue(replication.StorageID), VolumeID: types.StringValue(replication.VolumeID), } res, err := types.ObjectValueFrom(ctx, models.ItemReplication, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenDiskReplication struct to obj", err)) } tflog.Info(ctx, "End flattenDiskReplication") return res } func flattenResourceSnapshotExtend(ctx context.Context, snapshotList *compute.SnapshotExtendList) types.List { tflog.Info(ctx, "Start flattenResourceSnapshotExtend") tempSlice := make([]types.Object, 0, len(*snapshotList)) for _, snapshot := range *snapshotList { temp := models.ItemSnapshotExtendModel{ GUID: types.StringValue(snapshot.GUID), Label: types.StringValue(snapshot.Label), ReferenceID: types.StringValue(snapshot.ReferenceID), ResID: types.StringValue(snapshot.ResID), SnapSetGUID: types.StringValue(snapshot.SnapSetGUID), SnapSetTime: types.Int64Value(int64(snapshot.SnapSetTime)), TimeStamp: types.Int64Value(int64(snapshot.TimeStamp)), } obj, err := types.ObjectValueFrom(ctx, models.ItemSnapshot, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenResourceSnapshotExtend struct to obj", err)) } tempSlice = append(tempSlice, obj) } res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemSnapshot}, tempSlice) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenResourceSnapshotExtend", err)) } tflog.Info(ctx, "End flattenResourceSnapshotExtend") return res } func flattenResourceOSUsers(ctx context.Context, osUsersList *compute.ListOSUser) types.List { tflog.Info(ctx, "Start flattensOSUsers") tempSlice := make([]types.Object, 0, len(*osUsersList)) for _, osUser := range *osUsersList { temp := models.ItemOSUserModel{ GUID: types.StringValue(osUser.GUID), Login: types.StringValue(osUser.Login), Password: types.StringValue(osUser.Password), PubKey: types.StringValue(osUser.PubKey), } obj, err := types.ObjectValueFrom(ctx, models.ItemOSUsers, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattensOSUsers struct to obj", err)) } tempSlice = append(tempSlice, obj) } res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemOSUsers}, tempSlice) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattensOSUsers", err)) } tflog.Info(ctx, "End flattensOSUsers") return res } func flattenResourceDisks(ctx context.Context, disksList *compute.ListComputeDisks) types.List { tflog.Info(ctx, "Start flattenResourceDisks") tempSlice := make([]types.Object, 0, len(*disksList)) for _, disk := range *disksList { if disk.Type == "B" { continue } temp := flattenDisk(ctx, &disk) obj, err := types.ObjectValueFrom(ctx, models.ItemDisk, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenResourceDisks struct to obj", err)) } tempSlice = append(tempSlice, obj) } res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemDisk}, tempSlice) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenResourceDisks", err)) } tflog.Info(ctx, "End flattenResourceDisks") return res } func flattenResourceInterfaces(ctx context.Context, interfaces *compute.ListInterfaces) types.List { tflog.Info(ctx, "Start flattenResourceInterfaces") tempSlice := make([]types.Object, 0, len(*interfaces)) for _, item := range *interfaces { temp := models.ItemResourceInterfacesModel{ BusNumber: types.Int64Value(int64(item.BusNumber)), ConnID: types.Int64Value(int64(item.ConnID)), ConnType: types.StringValue(item.ConnType), GetGW: types.StringValue(item.DefGW), Enabled: types.BoolValue(item.Enabled), FLIPGroupID: types.Int64Value(int64(item.FLIPGroupID)), GUID: types.StringValue(item.GUID), IPAddress: types.StringValue(item.IPAddress), ListenSSH: types.BoolValue(item.ListenSSH), MAC: types.StringValue(item.MAC), MTU: types.Int64Value(int64(item.MTU)), Name: types.StringValue(item.Name), NetID: types.Int64Value(int64(item.NetID)), NetMask: types.Int64Value(int64(item.NetMask)), NetType: types.StringValue(item.NetType), NodeID: types.Int64Value(int64(item.NodeID)), PCISlot: types.Int64Value(item.PCISlot), QOS: flattenQOS(ctx, &item.QOS), LibvirtSettings: flattenLibvirtSetttings(ctx, &item.LibvirtSettings), Target: types.StringValue(item.Target), Type: types.StringValue(item.Type), VNFs: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, item.VNFs), } obj, err := types.ObjectValueFrom(ctx, models.ItemInterfaces, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenResourceInterfaces struct to obj", err)) } tempSlice = append(tempSlice, obj) } res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemInterfaces}, tempSlice) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenResourceInterfaces", err)) } tflog.Info(ctx, "End flattenInterfaces") return res } func flattenQOS(ctx context.Context, QOS *compute.QOS) types.Object { tflog.Info(ctx, "Start flattenQOS") temp := models.QOSModel{ ERate: types.Int64Value(int64(QOS.ERate)), GUID: types.StringValue(QOS.GUID), InBurst: types.Int64Value(int64(QOS.InBurst)), InRate: types.Int64Value(int64(QOS.InRate)), } res, err := types.ObjectValueFrom(ctx, models.ItemQos, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenQOS struct to obj", err)) } tflog.Info(ctx, "End flattenQOS") return res } func flattenLibvirtSetttings(ctx context.Context, settings *compute.LibvirtSettings) types.Object { tflog.Info(ctx, "Start flattenLibvirtSetttings") temp := models.LibvirtModel{ GUID: types.StringValue(settings.GUID), TXMode: types.StringValue(settings.TXMode), IOEventFD: types.StringValue(settings.IOEventFD), EventIDx: types.StringValue(settings.EventIDx), Queues: types.Int64Value(int64(settings.Queues)), RXQueueSize: types.Int64Value(int64(settings.RXQueueSize)), TXQueueSize: types.Int64Value(int64(settings.TXQueueSize)), } res, err := types.ObjectValueFrom(ctx, models.ItemLibvirtSettings, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenLibvirtSetttings struct to obj", err)) } tflog.Info(ctx, "End flattenLibvirtSetttings") return res } func flattenSnapSets(ctx context.Context, snapSets *compute.ListSnapSets) types.List { tflog.Info(ctx, "Start flattenSnapSets") tempSlice := make([]types.Object, 0, len(*snapSets)) for _, snapSet := range *snapSets { temp := models.ItemSnapSetModel{ Disks: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, snapSet.Disks), GUID: types.StringValue(snapSet.GUID), Label: types.StringValue(snapSet.Label), Timestamp: types.Int64Value(int64(snapSet.Timestamp)), } obj, err := types.ObjectValueFrom(ctx, models.ItemSnapSets, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenSnapSets struct to obj", err)) } tempSlice = append(tempSlice, obj) } res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemSnapSets}, tempSlice) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenSnapSets", err)) } tflog.Info(ctx, "End flattenSnapSets") return res } func flattenNetwork(ctx context.Context, networks types.Set, interfaces *compute.ListInterfaces) types.Set { tflog.Info(ctx, "Start flattenNetwork") tempSlice := make([]types.Object, 0, len(*interfaces)) for _, item := range *interfaces { temp := models.ItemNetworkModel{ NetType: types.StringValue(item.NetType), NetId: types.Int64Value(int64(item.NetID)), IpAddress: types.StringValue(item.IPAddress), Mac: types.StringValue(item.MAC), Weight: flattenNetworkWeight(ctx, networks, item), MTU: types.Int64Value(int64(item.MTU)), } obj, err := types.ObjectValueFrom(ctx, models.ItemNetwork, temp) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenNetwork struct to obj", err)) } tempSlice = append(tempSlice, obj) } res, err := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemNetwork}, tempSlice) if err != nil { tflog.Error(ctx, fmt.Sprint("Error flattenNetwork", err)) } tflog.Info(ctx, "End flattenNetwork") return res } func flattenNetworkWeight(ctx context.Context, networks types.Set, item compute.ItemVNFInterface) types.Int64 { tflog.Info(ctx, "Start flattenNetworkWeight") networkList := networks.Elements() for _, network := range networkList { networkMap := network.(types.Object).Attributes() if uint64(networkMap["net_id"].(types.Int64).ValueInt64()) == item.NetID && networkMap["net_type"].(types.String).ValueString() == item.NetType { return types.Int64Value(networkMap["weight"].(types.Int64).ValueInt64()) } } tflog.Info(ctx, "End flattenNetworkWeight") return types.Int64Value(0) }