From 0179a1ef45ddefe6f39337ce4a654ae99ee05faa Mon Sep 17 00:00:00 2001 From: Sergey Shubin svs1370 Date: Thu, 11 Mar 2021 00:06:55 +0300 Subject: [PATCH] Code adjustments during the 1st debug phase --- .gitignore | 1 + decort/data_source_account.go | 4 +- decort/data_source_compute.go | 138 +++++++++++++++++++++++++++++--- decort/data_source_disk.go | 4 +- decort/data_source_rg.go | 16 ++-- decort/interface_subresource.go | 5 +- decort/models_api.go | 20 ++--- decort/network_subresource.go | 6 +- decort/provider.go | 4 +- decort/quota_subresource.go | 22 ++--- decort/resource_compute.go | 2 + decort/resource_rg.go | 20 ++--- decort/utility_compute.go | 2 +- 13 files changed, 182 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index dad3eca..2e7cefe 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ decort/vendor/ +terraform-provider-decort* diff --git a/decort/data_source_account.go b/decort/data_source_account.go index c92ae65..735384f 100644 --- a/decort/data_source_account.go +++ b/decort/data_source_account.go @@ -84,13 +84,13 @@ func dataSourceAccount() *schema.Resource { Description: "Name of the account. Names are case sensitive and unique.", }, - "account_id": &schema.Schema{ + "account_id": { Type: schema.TypeInt, Optional: true, Description: "Unique ID of the account. If account ID is specified, then account name is ignored.", }, - "status": &schema.Schema{ + "status": { Type: schema.TypeString, Computed: true, Description: "Current status of the account.", diff --git a/decort/data_source_compute.go b/decort/data_source_compute.go index 61ceb99..1199d0f 100644 --- a/decort/data_source_compute.go +++ b/decort/data_source_compute.go @@ -35,11 +35,53 @@ import ( // "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) +func parseComputeDisksToExtraDisks(disks []DiskRecord) []interface{} { + // this return value will be used to d.Set("extra_disks",) item of dataSourceCompute schema, + // which is a simple list of integer disk IDs excluding boot disk ID + length := len(disks) + log.Debugf("parseComputeDisksToExtraDisks: called for %d disks", length) + + if length == 1 && disks[0].Type == "B" { + // there is only one disk in the list and it is a boot disk; + // as we skip boot disks, the result will be of 0 length + length = 0 + } + + result := make([]interface{}, length) + + if length == 0 { + return result + } + + idx := 0 + for _, value := range disks { + if value.Type == "B" { + // skip boot disk when iterating over the list of disks + continue + } + + result[idx] = value.ID + idx++ + } + + return result +} + func parseComputeDisks(disks []DiskRecord) []interface{} { + // return value was designed to d.Set("disks",) item of dataSourceCompute schema length := len(disks) log.Debugf("parseComputeDisks: called for %d disks", length) - + + /* + if length == 1 && disks[0].Type == "B" { + // there is only one disk in the list and it is a boot disk + // as we skip boot disks, the result will be of 0 lenght + length = 0 + } + */ + result := make([]interface{}, length) + if length == 0 { return result } @@ -47,6 +89,12 @@ func parseComputeDisks(disks []DiskRecord) []interface{} { elem := make(map[string]interface{}) for i, value := range disks { + /* + if value.Type == "B" { + // skip boot disk when parsing the list of disks + continue + } + */ // keys in this map should correspond to the Schema definition // as returned by dataSourceDiskSchemaMake() elem["name"] = value.Name @@ -66,14 +114,57 @@ func parseComputeDisks(disks []DiskRecord) []interface{} { result[i] = elem } - return result // this result will be used to d.Set("disks",) item of dataSourceCompute schema + return result +} + +func parseBootDiskSize(disks []DiskRecord) int { + // this return value will be used to d.Set("boot_disk_size",) item of dataSourceCompute schema + if len(disks) == 0 { + return 0 + } + + for _, value := range disks { + if value.Type == "B" { + return value.SizeMax + } + } + + return 0 +} + +func parseComputeInterfacesToNetworks(ifaces []InterfaceRecord) []interface{} { + // return value will be used to d.Set("networks",) item of dataSourceCompute schema + length := len(ifaces) + log.Debugf("parseComputeInterfacesToNetworks: called for %d ifaces", length) + + result := make([]interface{}, length) + + if length == 0 { + return result + } + + elem := make(map[string]interface{}) + + for i, value := range ifaces { + // Keys in this map should correspond to the Schema definition + // as returned by networkSubresourceSchemaMake() + elem["net_id"] = value.NetID + elem["net_type"] = value.NetType + elem["ip_address"] = value.IPAddress + + result[i] = elem + } + + return result } func parseComputeInterfaces(ifaces []InterfaceRecord) []interface{} { + // return value was designed to d.Set("interfaces",) item of dataSourceCompute schema length := len(ifaces) log.Debugf("parseComputeInterfaces: called for %d ifaces", length) result := make([]interface{}, length) + if length == 0 { return result } @@ -93,7 +184,7 @@ func parseComputeInterfaces(ifaces []InterfaceRecord) []interface{} { elem["connection_id"] = value.ConnID elem["connection_type"] = value.ConnType - /* TODO: add code to read in quota + /* TODO: add code to parse QoS qos_schema := interfaceQosSubresourceSchemaMake() qos_schema.Set("egress_rate", value.QOS.ERate) qos_schema.Set("ingress_rate", value.QOS.InRate) @@ -104,7 +195,7 @@ func parseComputeInterfaces(ifaces []InterfaceRecord) []interface{} { result[i] = elem } - return result // this result will be used to d.Set("interfaces",) item of dataSourceCompute schema + return result } func flattenCompute(d *schema.ResourceData, compFacts string) error { @@ -132,7 +223,8 @@ func flattenCompute(d *schema.ResourceData, compFacts string) error { d.Set("arch", model.Arch) d.Set("cpu", model.Cpu) d.Set("ram", model.Ram) - d.Set("boot_disk_size", model.BootDiskSize) + // d.Set("boot_disk_size", model.BootDiskSize) - bootdiskSize key in API compute/get is always zero, so we set boot_disk_size in another way + d.Set("boot_disk_size", parseBootDiskSize(model.Disks)) d.Set("image_id", model.ImageID) d.Set("description", model.Desc) d.Set("status", model.Status) @@ -140,14 +232,14 @@ func flattenCompute(d *schema.ResourceData, compFacts string) error { if len(model.Disks) > 0 { log.Debugf("flattenCompute: calling parseComputeDisks for %d disks", len(model.Disks)) - if err = d.Set("disks", parseComputeDisks(model.Disks)); err != nil { + if err = d.Set("extra_disks", parseComputeDisksToExtraDisks(model.Disks)); err != nil { return err } } if len(model.Interfaces) > 0 { log.Debugf("flattenCompute: calling parseComputeInterfaces for %d interfaces", len(model.Interfaces)) - if err = d.Set("interfaces", parseComputeInterfaces(model.Interfaces)); err != nil { + if err = d.Set("networks", parseComputeInterfacesToNetworks(model.Interfaces)); err != nil { return err } } @@ -258,6 +350,17 @@ func dataSourceCompute() *schema.Resource { Description: "This compute instance boot disk size in GB.", }, + "extra_disks": { + Type: schema.TypeList, + Computed: true, + MaxItems: MaxExtraDisksPerCompute, + Elem: &schema.Schema { + Type: schema.TypeInt, + }, + Description: "IDs of the extra disks attached to this compute.", + }, + + /* "disks": { Type: schema.TypeList, Computed: true, @@ -266,16 +369,19 @@ func dataSourceCompute() *schema.Resource { }, Description: "Detailed specification for all disks attached to this compute instance (including bood disk).", }, + */ - "guest_logins": { + "networks": { Type: schema.TypeList, - Computed: true, + Optional: true, + MaxItems: MaxNetworksPerCompute, Elem: &schema.Resource{ - Schema: loginsSubresourceSchemaMake(), + Schema: networkSubresourceSchemaMake(), }, - Description: "Details about the guest OS users provisioned together with this compute instance.", + Description: "Networks this compute is attached to.", }, + /* "interfaces": { Type: schema.TypeList, Computed: true, @@ -284,6 +390,16 @@ func dataSourceCompute() *schema.Resource { }, Description: "Specification for the virtual NICs configured on this compute instance.", }, + */ + + "guest_logins": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: loginsSubresourceSchemaMake(), + }, + Description: "Details about the guest OS users provisioned together with this compute instance.", + }, "description": { Type: schema.TypeString, diff --git a/decort/data_source_disk.go b/decort/data_source_disk.go index 3a9b618..16ccef2 100644 --- a/decort/data_source_disk.go +++ b/decort/data_source_disk.go @@ -39,7 +39,7 @@ func flattenDisk(d *schema.ResourceData, disk_facts string) error { // This function expects disk_facts string to contain a response from disks/get API // // NOTE: this function modifies ResourceData argument - as such it should never be called - // from resourceDiskExists(...) method + // from resourceDiskExists(...) method. Use utilityDiskCheckPresence instead. model := DiskRecord{} log.Debugf("flattenDisk: ready to unmarshal string %q", disk_facts) err := json.Unmarshal([]byte(disk_facts), &model) @@ -153,7 +153,7 @@ func dataSourceDiskSchemaMake() map[string]*schema.Schema { */ "sep_id": { - Type: schema.TypeString, + Type: schema.TypeInt, Computed: true, Description: "Storage end-point provider serving this disk.", }, diff --git a/decort/data_source_rg.go b/decort/data_source_rg.go index 7048bc4..8220593 100644 --- a/decort/data_source_rg.go +++ b/decort/data_source_rg.go @@ -101,31 +101,31 @@ func dataSourceResgroup() *schema.Resource { Description: "Name of the resource group. Names are case sensitive and unique within the context of an account.", }, - "rg_id": &schema.Schema{ + "rg_id": { Type: schema.TypeInt, Optional: true, Description: "Unique ID of the resource group. If this ID is specified, then resource group name is ignored.", }, - "account_name": &schema.Schema{ + "account_name": { Type: schema.TypeString, Optional: true, Description: "Name of the account, which this resource group belongs to.", }, - "account_id": &schema.Schema{ + "account_id": { Type: schema.TypeInt, Optional: true, Description: "Unique ID of the account, which this resource group belongs to. If account ID is specified, then account name is ignored.", }, - "description": &schema.Schema{ + "description": { Type: schema.TypeString, Computed: true, Description: "User-defined text description of this resource group.", }, - "grid_id": &schema.Schema{ + "grid_id": { Type: schema.TypeInt, Computed: true, Description: "Unique ID of the grid, where this resource group is deployed.", @@ -133,7 +133,7 @@ func dataSourceResgroup() *schema.Resource { "quota": { Type: schema.TypeList, - Optional: true, + Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: quotaRgSubresourceSchemaMake(), // this is a dictionary @@ -147,13 +147,13 @@ func dataSourceResgroup() *schema.Resource { Description: "Current status of this resource group.", }, - "def_net_type": &schema.Schema{ + "def_net_type": { Type: schema.TypeString, Computed: true, Description: "Type of the default network for this resource group.", }, - "def_net_id": &schema.Schema{ + "def_net_id": { Type: schema.TypeInt, Computed: true, Description: "ID of the default network for this resource group (if any).", diff --git a/decort/interface_subresource.go b/decort/interface_subresource.go index 09941e0..58aa6a9 100644 --- a/decort/interface_subresource.go +++ b/decort/interface_subresource.go @@ -89,11 +89,12 @@ func interfaceSubresourceSchemaMake() map[string]*schema.Schema { }, "qos": { - Computed: true, + Type: schema.TypeList, + Computed: true, Elem: &schema.Resource{ Schema: interfaceQosSubresourceSchemaMake(), }, - Description: "Details about the guest OS users provisioned together with this compute instance.", + Description: "QoS settings for this interface.", }, } diff --git a/decort/models_api.go b/decort/models_api.go index c5d7379..0b34a05 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -105,12 +105,12 @@ type ResgroupUpdateParam struct { // structures related to /cloudapi/rg/get API call // type QuotaRecord struct { // this is how quota is reported by /api/.../rg/get - Cpu int `json:"CU_C"` // CPU count in pcs - Ram int `json:"CU_M"` // RAM volume in MB - Disk int `json:"CU_D"` // Disk capacity in GB - ExtIPs int `json:"CU_I"` // Ext IPs count - ExtTraffic int `json:"CU_NP"` // Ext network traffic - GpuUnits int `json:"gpu_units"` // GPU count + Cpu int `json:"CU_C"` // CPU count in pcs + Ram float32 `json:"CU_M"` // RAM volume in MB, it is STILL reported as FLOAT + Disk int `json:"CU_D"` // Disk capacity in GB + ExtIPs int `json:"CU_I"` // Ext IPs count + ExtTraffic int `json:"CU_NP"` // Ext network traffic + GpuUnits int `json:"gpu_units"` // GPU count } type ResourceRecord struct { // this is how actual usage is reported by /api/.../rg/get @@ -245,7 +245,7 @@ type ComputeRecord struct { AccountName string `json:"accountName"` ACLs []UserAclRecord `json:"acl"` Arch string `json:"arch"` - BootDiskSize int `json:"bootdiskSize"` + BootDiskSize int `json:"bootdiskSize"` // NOTE: this key is always 0 in compute/get API response CloneReference int `json:"cloneReference"` Clones []int `json:"clones"` Cpus int `json:"cpus"` @@ -258,7 +258,7 @@ type ComputeRecord struct { GridID int `json:"gid"` ID uint `json:"id"` ImageID int `json:"imageId"` - Interfaces []InterfaceRecord `json:"interfaces` + Interfaces []InterfaceRecord `json:"interfaces"` LockStatus string `json:"lockStatus"` ManagerID int `json:"managerId"` Name string `json:"name"` @@ -297,7 +297,7 @@ type DiskRecord struct { // ACLs `json:"ACL"` - it is a dictionary, special parsing required // was - Acl map[string]string `json:"acl"` AccountID int `json:"accountId"` - AccountName string `json:"accountName"` // NOTE: absent from compute/get output + AccountName string `json:"accountName"` // NOTE: absent from compute/get output BootPartition int `json:"bootPartition"` CreatedTime uint64 `json:"creationTime"` DeletedTime uint64 `json:"deletionTime"` @@ -320,7 +320,7 @@ type DiskRecord struct { PurgeTime uint64 `json:"purgeTime"` // Role string `json:"role"` SepType string `json:"sepType"` - SepID int `json:"sepid"` + SepID int `json:"sepid"` // NOTE: absent from compute/get output SizeMax int `json:"sizeMax"` SizeUsed int `json:"sizeUsed"` // sum over all snapshots of this disk to report total consumed space Snapshots []SnapshotRecord `json:"snapshots"` diff --git a/decort/network_subresource.go b/decort/network_subresource.go index fff60db..21a96d3 100644 --- a/decort/network_subresource.go +++ b/decort/network_subresource.go @@ -32,19 +32,19 @@ import ( func networkSubresourceSchemaMake() map[string]*schema.Schema { rets := map[string]*schema.Schema{ - "net_type": &schema.Schema{ + "net_type": { Type: schema.TypeString, Required: true, Description: "Type of the network for this connection, either EXTNET or VINS.", }, - "net_id": &schema.Schema{ + "net_id": { Type: schema.TypeInt, Required: true, Description: "ID of the network for this connection.", }, - "ipaddr": &schema.Schema{ + "ip_address": { Type: schema.TypeString, Optional: true, Description: "Optional IP address to assign to this connection. This IP should belong to the selected network and free for use.", diff --git a/decort/provider.go b/decort/provider.go index c07dc31..cd829a3 100644 --- a/decort/provider.go +++ b/decort/provider.go @@ -99,7 +99,7 @@ func Provider() *schema.Provider { ResourcesMap: map[string]*schema.Resource{ "decort_resgroup": resourceResgroup(), - // "decort_kvmvm": resourceCompute(), + "decort_kvmvm": resourceCompute(), // "decort_disk": resourceDisk(), // "decort_vins": resourceVins(), // "decort_pfw": resourcePfw(), @@ -110,7 +110,7 @@ func Provider() *schema.Provider { "decort_resgroup": dataSourceResgroup(), "decort_kvmvm": dataSourceCompute(), "decort_image": dataSourceImage(), - "decort_disk": dataSourceDisk(), + // "decort_disk": dataSourceDisk(), // "decort_vins": dataSourceVins(), // "decort_pfw": dataSourcePfw(), }, diff --git a/decort/quota_subresource.go b/decort/quota_subresource.go index 1294b75..cc6b14c 100644 --- a/decort/quota_subresource.go +++ b/decort/quota_subresource.go @@ -31,7 +31,7 @@ import ( func makeQuotaRecord(arg_list []interface{}) (QuotaRecord, int) { quota := QuotaRecord{ Cpu: -1, - Ram: -1, + Ram: -1., Disk: -1, ExtTraffic: -1, ExtIPs: -1, @@ -48,7 +48,7 @@ func makeQuotaRecord(arg_list []interface{}) (QuotaRecord, int) { } if subres_data["ram"].(int) > 0 { - quota.Ram = subres_data["ram"].(int) // RAM volume in MB + quota.Ram = subres_data["ram"].(float32) // RAM volume in MB, as float! } if subres_data["ext_traffic"].(int) > 0 { @@ -70,7 +70,7 @@ func parseQuota(quota QuotaRecord) []interface{} { quota_map := make(map[string]interface{}) quota_map["cpu"] = quota.Cpu - quota_map["ram"] = quota.Ram + quota_map["ram"] = quota.Ram // MB; this is float32, unlike the rest of values quota_map["disk"] = quota.Disk quota_map["ext_traffic"] = quota.ExtTraffic quota_map["ext_ips"] = quota.ExtIPs @@ -84,42 +84,42 @@ func parseQuota(quota QuotaRecord) []interface{} { func quotaRgSubresourceSchemaMake() map[string]*schema.Schema { rets := map[string]*schema.Schema{ - "cpu": &schema.Schema{ + "cpu": { Type: schema.TypeInt, Optional: true, Default: -1, Description: "Limit on the total number of CPUs in this resource group.", }, - "ram": &schema.Schema{ - Type: schema.TypeInt, // NB: old API expects and returns this as float! This may be changed in the future. + "ram": { + Type: schema.TypeFloat, // NB: API expects and returns this as float in units of MB! This may be changed in the future. Optional: true, - Default: -1, + Default: -1., Description: "Limit on the total amount of RAM in this resource group, specified in MB.", }, - "disk": &schema.Schema{ + "disk": { Type: schema.TypeInt, Optional: true, Default: -1, Description: "Limit on the total volume of storage resources in this resource group, specified in GB.", }, - "ext_traffic": &schema.Schema{ + "ext_traffic": { Type: schema.TypeInt, Optional: true, Default: -1, Description: "Limit on the total ingress network traffic for this resource group, specified in GB.", }, - "ext_ips": &schema.Schema{ + "ext_ips": { Type: schema.TypeInt, Optional: true, Default: -1, Description: "Limit on the total number of external IP addresses this resource group can use.", }, - "gpu_units": &schema.Schema{ + "gpu_units": { Type: schema.TypeInt, Optional: true, Default: -1, diff --git a/decort/resource_compute.go b/decort/resource_compute.go index 2bfa765..5c77066 100644 --- a/decort/resource_compute.go +++ b/decort/resource_compute.go @@ -341,6 +341,7 @@ func resourceCompute() *schema.Resource { Description: "Name of the account this compute instance belongs to.", }, + /* "disks": { Type: schema.TypeList, Computed: true, @@ -358,6 +359,7 @@ func resourceCompute() *schema.Resource { }, Description: "Specification for the virtual NICs configured on this compute instance.", }, + */ "guest_logins": { Type: schema.TypeList, diff --git a/decort/resource_rg.go b/decort/resource_rg.go index 04e6ae7..315bdce 100644 --- a/decort/resource_rg.go +++ b/decort/resource_rg.go @@ -174,7 +174,7 @@ func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error { url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotarecord_new.Disk)) } - if quotarecord_new.Ram != quotarecord_old.Ram { + if quotarecord_new.Ram != quotarecord_old.Ram { // NB: quota on RAM is stored as float32, in units of MB do_update = true log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotarecord_new.Ram, quotarecord_old.Ram) url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotarecord_new.Ram)) @@ -275,57 +275,57 @@ func resourceResgroup() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ + "name": { Type: schema.TypeString, Required: true, Description: "Name of this resource group. Names are case sensitive and unique within the context of a account.", }, - "account_id": &schema.Schema{ + "account_id": { Type: schema.TypeInt, Optional: true, Description: "Unique ID of the account, which this resource group belongs to. If account ID is specified, then account name is ignored.", }, - "account_name": &schema.Schema{ + "account_name": { Type: schema.TypeString, Optional: true, Description: "Name of the account, which this resource group belongs to.", }, - "def_net_type": &schema.Schema{ + "def_net_type": { Type: schema.TypeString, Optional: true, Default: "PRIVATE", Description: "Type of the network, which this resource group will use as default for its computes - PRIVATE or PUBLIC or NONE.", }, - "def_net_id": &schema.Schema{ + "def_net_id": { Type: schema.TypeInt, Computed: true, Description: "ID of the default network for this resource group (if any).", }, - "ipcidr": &schema.Schema{ + "ipcidr": { Type: schema.TypeString, Optional: true, Description: "Address of the netowrk inside the private network segment (aka ViNS) if def_net_type=PRIVATE", }, - "ext_net_id": &schema.Schema{ + "ext_net_id": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "ID of the external network, which this resource group will use as default for its computes if def_net_type=PUBLIC", }, - "ext_ip": &schema.Schema{ + "ext_ip": { Type: schema.TypeString, Optional: true, Description: "IP address on the external netowrk to request, if def_net_type=PUBLIC", }, - "grid_id": &schema.Schema{ // change of Grid ID will require new RG + "grid_id": { // change of Grid ID will require new RG Type: schema.TypeInt, Required: true, Description: "Unique ID of the grid, where this resource group is deployed.", diff --git a/decort/utility_compute.go b/decort/utility_compute.go index 1fe546f..ff14d83 100644 --- a/decort/utility_compute.go +++ b/decort/utility_compute.go @@ -167,7 +167,7 @@ func (ctrl *ControllerCfg) utilityComputeNetworksConfigure(d *schema.ResourceDat urlValues.Add("computeId", d.Id()) urlValues.Add("netType", net_data["net_type"].(string)) urlValues.Add("netId", fmt.Sprintf("%d", net_data["net_id"].(int))) - ipaddr, ipSet := net_data["ipaddr"] // "ipaddr" key is optional + ipaddr, ipSet := net_data["ip_address"] // "ip_address" key is optional if ipSet { urlValues.Add("ipAddr", ipaddr.(string)) }