End of day commit, interim, no testing yet

rc-1.0
Sergey Shubin svs1370 4 years ago
parent 871a5e06ee
commit 1490c543de

@ -38,7 +38,7 @@ import (
func flattenCompute(d *schema.ResourceData, comp_facts string) error { func flattenCompute(d *schema.ResourceData, comp_facts string) error {
// NOTE: this function modifies ResourceData argument - as such it should never be called // NOTE: this function modifies ResourceData argument - as such it should never be called
// from resourceComputeExists(...) method // from resourceComputeExists(...) method
model := MachinesGetResp{} model := ComputeGetResp{}
log.Printf("flattenCompute: ready to unmarshal string %q", comp_facts) log.Printf("flattenCompute: ready to unmarshal string %q", comp_facts)
err := json.Unmarshal([]byte(comp_facts), &model) err := json.Unmarshal([]byte(comp_facts), &model)
if err != nil { if err != nil {
@ -50,11 +50,17 @@ func flattenCompute(d *schema.ResourceData, comp_facts string) error {
d.SetId(fmt.Sprintf("%d", model.ID)) d.SetId(fmt.Sprintf("%d", model.ID))
d.Set("name", model.Name) d.Set("name", model.Name)
d.Set("rgid", model.ResGroupID) d.Set("rgid", model.ResGroupID)
d.Set("rg_name", model.ResGroupName)
d.Set("account_id", model.AccountID)
d.Set("account_name", model.AccountName)
d.Set("arch", model.Arch)
d.Set("cpu", model.Cpu) d.Set("cpu", model.Cpu)
d.Set("ram", model.Ram) d.Set("ram", model.Ram)
// d.Set("boot_disk", model.BootDisk) d.Set("boot_disk_size", model.BootDiskSize)
d.Set("image_id", model.ImageID) d.Set("image_id", model.ImageID)
d.Set("description", model.Description) d.Set("description", model.Desc)
d.Set("status", model.Status)
d.Set("tech_status", model.TechStatus)
bootdisk_map := make(map[string]interface{}) bootdisk_map := make(map[string]interface{})
bootdisk_map["size"] = model.BootDisk bootdisk_map["size"] = model.BootDisk
@ -131,76 +137,86 @@ func dataSourceCompute() *schema.Resource {
"name": { "name": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
Description: "Name of this virtual machine. This parameter is case sensitive.", Description: "Name of this compute instance. NOTE: this parameter is case sensitive.",
}, },
"rgid": { "rgid": {
Type: schema.TypeInt, Type: schema.TypeInt,
Required: true, Required: true,
ValidateFunc: validation.IntAtLeast(1), ValidateFunc: validation.IntAtLeast(1),
Description: "ID of the resource group where this virtual machine is located.", Description: "ID of the resource group where this compute instance is located.",
}, },
/* "rg_name": {
"internal_ip": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Internal IP address of this Compute.", Description: "Name of the resource group where this compute instance is located.",
},
"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.",
},
"arch": {
Type: schema.TypeString,
Computed: true,
Description: "Hardware architecture of this compute instance.",
}, },
*/
"cpu": { "cpu": {
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "Number of CPUs allocated for this virtual machine.", Description: "Number of CPUs allocated for this compute instance.",
}, },
"ram": { "ram": {
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "Amount of RAM in MB allocated for this virtual machine.", Description: "Amount of RAM in MB allocated for this compute instance.",
}, },
"image_id": { "image_id": {
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "ID of the OS image this virtual machine is based on.", Description: "ID of the OS image this compute instance is based on.",
}, },
/*
"image_name": { "image_name": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Name of the OS image this virtual machine is based on.", Description: "Name of the OS image this compute instance is based on.",
}, },
*/
"boot_disk": { "boot_disk_size": {
Type: schema.TypeList, Type: schema.TypeInt,
Computed: true, Computed: true,
MinItems: 1, Description: "This compute instance boot disk size in GB.",
Elem: &schema.Resource {
Schema: diskSubresourceSchema(),
},
Description: "Specification for a boot disk on this virtual machine.",
}, },
"data_disks": { "disks": {
Type: schema.TypeList, Type: schema.TypeList,
Computed: true, Computed: true,
Elem: &schema.Resource { Elem: &schema.Resource {
Schema: diskSubresourceSchema(), Schema: diskSubresourceSchema(), // ID, type, name, size, account ID, SEP ID, SEP type, pool, status, tech status, compute ID, image ID
}, },
Description: "Specification for data disks on this virtual machine.", Description: "Detailed specification for all disks attached to this compute instance (including bood disk).",
}, },
"guest_logins": { "guest_logins": {
Type: schema.TypeList, Type: schema.TypeList,
Computed: true, Computed: true,
Elem: &schema.Resource { Elem: &schema.Resource {
Schema: loginsSubresourceSchema(), Schema: guestLoginsSubresourceSchema(),
}, },
Description: "Specification for guest logins on this virtual machine.", Description: "Details about the guest OS users provisioned together with this compute instance.",
}, },
"networks": { "networks": {
@ -224,22 +240,28 @@ func dataSourceCompute() *schema.Resource {
"description": { "description": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Description of this virtual machine.", Description: "User-defined text description of this compute instance.",
}, },
"user": { "status": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Default login name for the guest OS on this virtual machine.", Description: "Current model status of this compute instance.",
}, },
"password": { "tech_status": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Sensitive: true, Description: "Current technical status of this compute instance.",
Description: "Default password for the guest OS login on this virtual machine.",
}, },
/*
"internal_ip": {
Type: schema.TypeString,
Computed: true,
Description: "Internal IP address of this Compute.",
},
*/
}, },
} }
} }

@ -54,6 +54,12 @@ func flattenResgroup(d *schema.ResourceData, rg_facts string) error {
d.Set("name", details.Name) d.Set("name", details.Name)
d.Set("account_id", details.AccountID) d.Set("account_id", details.AccountID)
d.Set("grid_id", details.GridID) d.Set("grid_id", details.GridID)
d.Set("desc", details.Description)
d.Set("status", details.Status)
d.Set("def_net", details.DefaultNetType)
d.Set("def_net_id", details.DefaultNetID)
d.Set("vins", details.Vins)
d.Set("computes", details.Computes)
log.Debugf("flattenResgroup: calling flattenQuota()") log.Debugf("flattenResgroup: calling flattenQuota()")
if err = d.Set("quotas", flattenQuota(details.Quotas)); err != nil { if err = d.Set("quotas", flattenQuota(details.Quotas)); err != nil {
@ -91,19 +97,25 @@ func dataSourceResgroup() *schema.Resource {
"name": { "name": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
Description: "Name of this resource group. Names are case sensitive and unique within the context of a tenant.", Description: "Name of this resource group. Names are case sensitive and unique within the context of an account.",
}, },
"account": &schema.Schema { "account": &schema.Schema {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
Description: "Name of the tenant, which this resource group belongs to.", Description: "Name of the account, which this resource group belongs to.",
}, },
"account_id": &schema.Schema { "account_id": &schema.Schema {
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "Unique ID of the tenant, which this resource group belongs to.", Description: "Unique ID of the account, which this resource group belongs to.",
},
"desc": &schema.Schema {
Type: schema.TypeString,
Computed: true,
Description: "User-defined text description of this resource group.",
}, },
"grid_id": &schema.Schema { "grid_id": &schema.Schema {
@ -112,21 +124,52 @@ func dataSourceResgroup() *schema.Resource {
Description: "Unique ID of the grid, where this resource group is deployed.", Description: "Unique ID of the grid, where this resource group is deployed.",
}, },
"public_ip": { // this may be obsoleted as new network segments and true resource groups are implemented
Type: schema.TypeString,
Computed: true,
Description: "Public IP address of this resource group (if any).",
},
"quotas": { "quotas": {
Type: schema.TypeList, Type: schema.TypeList,
Optional: true, Optional: true,
MaxItems: 1, MaxItems: 1,
Elem: &schema.Resource { Elem: &schema.Resource {
Schema: quotasSubresourceSchema(), Schema: quotaRgSubresourceSchema(), // this is a dictionary
}, },
Description: "Quotas on the resources for this resource group.", Description: "Quotas on the resources for this resource group.",
}, },
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current status of this resource group.",
},
"def_net": &schema.Schema {
Type: schema.TypeString,
Computed: true,
Description: "Type of the default network for this resource group.",
},
"def_net_id": &schema.Schema {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the default network for this resource group (if any).",
},
"vins": {
Type: schema.TypeList, // this is a list of ints
Computed: true,
MaxItems: LimitMaxVinsPerResgroup,
Elem: &schema.Schema {
Type: schema.TypeInt,
},
Description: "List of VINs deployed in this resource group.",
},
"computes": {
Type: schema.TypeList, //t his is a list of ints
Computed: true,
Elem: &schema.Schema {
Type: schema.TypeInt,
},
Description: "List of computes deployed in this resource group."
},
}, },
} }
} }

@ -89,12 +89,13 @@ func makeDataDisksArgString(disks []DiskConfig) string {
} }
*/ */
// ID, type, name, size, account ID, SEP ID, SEP type, pool, status, tech status, compute ID, image ID
func diskSubresourceSchema() map[string]*schema.Schema { func diskSubresourceSchema() map[string]*schema.Schema {
rets := map[string]*schema.Schema { rets := map[string]*schema.Schema {
"label": { "name": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
Description: "Unique label to identify this disk among other disks connected to this VM.", Description: "Name of this disk resource.",
}, },
"size": { "size": {
@ -104,24 +105,37 @@ func diskSubresourceSchema() map[string]*schema.Schema {
Description: "Size of the disk in GB.", Description: "Size of the disk in GB.",
}, },
"pool": { "account_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the account this disk resource belongs to.",
},
"sep_id": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Default: "default", Default: "default",
Description: "Pool from which this disk should be provisioned.", Description: "Storage provider (storage technology type) by which this disk should be served.",
}, },
"provider": { "sep_type": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Default: "default", Default: "default",
Description: "Storage provider (storage technology type) by which this disk should be served.", Description: "Storage provider (storage technology type) by which this disk should be served.",
}, },
"disk_id": { "pool": {
Type: schema.TypeString,
Optional: true,
Default: "default",
Description: "Pool from which this disk should be provisioned.",
},
"image_id": {
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "ID of this disk resource.", Description: "ID of the binary Image this disk resource is cloned from (if any).",
}, },
} }

@ -1,5 +1,5 @@
/* /*
Copyright (c) 2019-2020 Digital Energy Cloud Solutions LLC. All Rights Reserved. Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com> Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -104,7 +104,16 @@ type ResgroupUpdateParam struct {
// //
// structures related to /cloudapi/rg/get API call // structures related to /cloudapi/rg/get API call
// //
type ResourceRecord struct { 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
}
type ResourceRecord struct { // this is how actual usage is reported by /api/.../rg/get
Cpu int `json:"cpu"` Cpu int `json:"cpu"`
Disk int `json:"disksize"` Disk int `json:"disksize"`
ExtIPs int `json:"extips"` ExtIPs int `json:"extips"`
@ -135,7 +144,7 @@ type ResgroupGetResp struct {
ID uint `json:"id"` ID uint `json:"id"`
LockStatus string `json:"lockStatus"` LockStatus string `json:"lockStatus"`
Name string `json:"name"` Name string `json:"name"`
Quotas QuotaRecord `json:"resourceLimits"` Quota QuotaRecord `json:"resourceLimits"`
Status string `json:"status"` Status string `json:"status"`
UpdatedBy string `json:"updatedBy"` UpdatedBy string `json:"updatedBy"`
UpdatedTime uint64 `json:"updatedTime"` UpdatedTime uint64 `json:"updatedTime"`
@ -171,7 +180,7 @@ const ResgroupDeleteAPI = "/restmachine/cloudapi/rg/delete"
// //
const KvmX86CreateAPI = "/restmachine/cloudapi/kvmx86/create" const KvmX86CreateAPI = "/restmachine/cloudapi/kvmx86/create"
const KvmPPCCreateAPI = "/restmachine/cloudapi/kvmppc/create" const KvmPPCCreateAPI = "/restmachine/cloudapi/kvmppc/create"
type KvmXXXCreateParam struct { // this is unified structure for both x86 and PPC based VMs creation type KvmVmCreateParam struct { // this is unified structure for both x86 and PPC based KVM VMs creation
RgID uint `json:"rgId"` RgID uint `json:"rgId"`
Name string `json:"name"` Name string `json:"name"`
Cpu int `json:"cpu"` Cpu int `json:"cpu"`
@ -320,9 +329,6 @@ type OsUserRecord struct {
} }
const ComputeGetAPI = "/restmachine/cloudapi/compute/get" const ComputeGetAPI = "/restmachine/cloudapi/compute/get"
type ComputeGetParam struct {
ComputeID int `json:"computeId"`
}
type ComputeGetResp struct { type ComputeGetResp struct {
// ACLs `json:"ACL"` - it is a dictionary, special parsing required // ACLs `json:"ACL"` - it is a dictionary, special parsing required
AccountID int `json:"accountId"` AccountID int `json:"accountId"`

@ -29,13 +29,14 @@ import (
) )
func makeQuotaConfig(arg_list []interface{}) (ResgroupQuotaConfig, int) { func makeQuotaRecord(arg_list []interface{}) (QuotaRecord, int) {
quota := ResgroupQuotaConfig{ quota := QuotaRecord{
Cpu: -1, Cpu: -1,
Ram: -1, Ram: -1,
Disk: -1, Disk: -1,
NetTraffic: -1, NetTraffic: -1,
ExtIPs: -1, ExtIPs: -1,
GpuUnits: -1,
} }
subres_data := arg_list[0].(map[string]interface{}) subres_data := arg_list[0].(map[string]interface{})
@ -44,75 +45,86 @@ func makeQuotaConfig(arg_list []interface{}) (ResgroupQuotaConfig, int) {
} }
if subres_data["disk"].(int) > 0 { if subres_data["disk"].(int) > 0 {
quota.Disk = subres_data["disk"].(int) quota.Disk = subres_data["disk"].(int) // Disk capacity ib GB
} }
if subres_data["ram"].(int) > 0 { if subres_data["ram"].(int) > 0 {
ram_limit := subres_data["ram"].(int) quota.Ram= subres_data["ram"].(int) // RAM volume in MB
quota.Ram = float32(ram_limit) // /1024 // legacy fix - this can be obsoleted once redmine FR #1465 is implemented
} }
if subres_data["net_traffic"].(int) > 0 { if subres_data["ext_traffic"].(int) > 0 {
quota.NetTraffic = subres_data["net_traffic"].(int) quota.ExtTraffic = subres_data["ext_traffic"].(int)
} }
if subres_data["ext_ips"].(int) > 0 { if subres_data["ext_ips"].(int) > 0 {
quota.ExtIPs = subres_data["ext_ips"].(int) quota.ExtIPs = subres_data["ext_ips"].(int)
} }
if subres_data["gpu_units"].(int) > 0 {
quota.GpuUnits = subres_data["gpu_units"].(int)
}
return quota, 1 return quota, 1
} }
func flattenQuota(quotas QuotaRecord) []interface{} { func flattenQuota(quota QuotaRecord) []interface{} {
quotas_map := make(map[string]interface{}) quota_map := make(map[string]interface{})
quotas_map["cpu"] = quotas.Cpu quota_map["cpu"] = quota.Cpu
quotas_map["ram"] = int(quotas.Ram) quota_map["ram"] = quota.Ram
quotas_map["disk"] = quotas.Disk quota_map["disk"] = quota.Disk
quotas_map["net_traffic"] = quotas.NetTraffic quota_map["ext_traffic"] = quota.ExtTraffic
quotas_map["ext_ips"] = quotas.ExtIPs quota_map["ext_ips"] = quota.ExtIPs
quota_map["gpu_units"] = quota.GpuUnits
result := make([]interface{}, 1) result := make([]interface{}, 1)
result[0] = quotas_map result[0] = quota_map
return result return result
} }
func quotasSubresourceSchema() map[string]*schema.Schema { func quotaRgSubresourceSchema() map[string]*schema.Schema {
rets := map[string]*schema.Schema { rets := map[string]*schema.Schema {
"cpu": &schema.Schema { "cpu": &schema.Schema {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
Default: -1, Default: -1,
Description: "The quota on the total number of CPUs in this resource group.", Description: "Limit on the total number of CPUs in this resource group.",
}, },
"ram": &schema.Schema { "ram": &schema.Schema {
Type: schema.TypeInt, // NB: API expects and returns this as float! This may be changed in the future. Type: schema.TypeInt, // NB: old API expects and returns this as float! This may be changed in the future.
Optional: true, Optional: true,
Default: -1, Default: -1,
Description: "The quota on the total amount of RAM in this resource group, specified in GB (Gigabytes!).", Description: "Limit on the total amount of RAM in this resource group, specified in MB.",
}, },
"disk": &schema.Schema { "disk": &schema.Schema {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
Default: -1, Default: -1,
Description: "The quota on the total volume of storage resources in this resource group, specified in GB.", Description: "Limit on the total volume of storage resources in this resource group, specified in GB.",
}, },
"net_traffic": &schema.Schema { "ext_traffic": &schema.Schema {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
Default: -1, Default: -1,
Description: "The quota on the total ingress network traffic for this resource group, specified in GB.", Description: "Limit on the total ingress network traffic for this resource group, specified in GB.",
}, },
"ext_ips": &schema.Schema { "ext_ips": &schema.Schema {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
Default: -1, Default: -1,
Description: "The quota on the total number of external IP addresses this resource group can use.", Description: "Limit on the total number of external IP addresses this resource group can use.",
},
"gpu_units": &schema.Schema {
Type: schema.TypeInt,
Optional: true,
Default: -1,
Description: "Limit on the total number of virtual GPUs this resource group can use.",
}, },
} }
return rets return rets

@ -1,5 +1,5 @@
/* /*
Copyright (c) 2019-2020 Digital Energy Cloud Solutions. All Rights Reserved. Copyright (c) 2019-2021 Digital Energy Cloud Solutions. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -29,49 +29,54 @@ import (
"log" "log"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
) )
func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error { func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
log.Debugf("resourceResgroupCreate: called for res group name %q, account name %q", // First validate that we have all parameters required to create the new Resource Group
d.Get("name").(string), d.Get("account").(string)) arg_set := false
account_name, arg_set := d.GetOk("account")
rg := &ResgroupConfig{ if !arg_set {
Name: d.Get("name").(string), return fmt.Errorf("Cannot create new RG: missing account.")
AccountName: d.Get("account").(string),
} }
rg_name, arg_set := d.GetOk("name")
// validate that we have all parameters required to create the new Resource Group if !arg_set {
// location code is required to create new resource group return fmt.Errorf("Cannot create new RG: missing name.")
arg_value, arg_set := d.GetOk("location")
if arg_set {
rg.Location = arg_value.(string)
} else {
return fmt.Errorf("Cannot create new RG %q for account %q: missing location parameter.",
rg.Name, rg.AccountName)
} }
// account ID is required to create new resource group grid_id, arg_set := d.GetOk("grid_id")
if !arg_set {
return fmt.Errorf("Cannot create new RG %q for account %q: missing Grid ID.",
rg_name.(string), account_name.(string))
}
// all required parameters are set in the schema - we can continue with RG creation
log.Debugf("resourceResgroupCreate: called for RG name %q, account name %q",
account_name.(string), rg_name.(string))
// Valid account ID is required to create new resource group
// obtain Account ID by account name - it should not be zero on success // obtain Account ID by account name - it should not be zero on success
account_id, err := utilityGetAccountIdByName(rg.AccountName, m) validated_account_id, err := utilityGetAccountIdByName(account_name.(string), m)
if err != nil { if err != nil {
return err return err
} }
rg.AccountID = account_id
set_quotas := false // quota settings are optional
arg_value, arg_set = d.GetOk("quotas") set_quota := false
var quota_record QuotaRecord
arg_value, arg_set = d.GetOk("quota")
if arg_set { if arg_set {
log.Debugf("resourceResgroupCreate: calling makeQuotaConfig") log.Debugf("resourceResgroupCreate: setting Quota on RG requested")
rg.Quota, _ = makeQuotaConfig(arg_value.([]interface{})) quota_record, _ = makeQuotaRecord(arg_value.([]interface{}))
set_quotas = true set_quota = true
} }
controller := m.(*ControllerCfg) controller := m.(*ControllerCfg)
log.Debugf("resourceResgroupCreate: called by user %q for RG name %q, for account %q / ID %d, location %q", log.Debugf("resourceResgroupCreate: called by user %q for RG name %q, account %q / ID %d, Grid ID %d",
controller.getdecortUsername(), controller.getdecortUsername(),
rg.Name, d.Get("account").(string), rg.AccountID, rg.Location) rg_name.(string), account_name.(string), validated_account_id, gird_id.(int))
/* /*
type ResgroupCreateParam struct { type ResgroupCreateParam struct {
AccountID int `json:"accountId"` AccountID int `json:"accountId"`
@ -93,23 +98,40 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
*/ */
url_values := &url.Values{} url_values := &url.Values{}
url_values.Add("accountId", fmt.Sprintf("%d", rg.AccountID)) url_values.Add("accountId", fmt.Sprintf("%d", validated_account_id))
url_values.Add("name", rg.Name) url_values.Add("name", rg_name.(string))
url_values.Add("gid", rg.Location) url_values.Add("gid", fmt.Sprintf("%d", grid_id.(int)))
url_values.Add("owner", controller.getdecortUsername()) url_values.Add("owner", controller.getdecortUsername())
url_values.Add("def_net", "NONE")
// pass quota values as set // pass quota values as set
if set_quotas { if set_quota {
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", rg.Quota.Cpu)) url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quota_record.Cpu))
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", rg.Quota.Disk)) url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quota_record.Disk))
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", rg.Quota.Ram)) url_values.Add("maxMemoryCapacity", fmt.Sprintf("%d", quota_record.Ram))
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", rg.Quota.NetTraffic)) url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quota_record.ExtTraffic))
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", rg.Quota.ExtIPs)) url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quota_record.ExtIPs))
// url_values.Add("???", fmt.Sprintf("%d", quota_record.GpuUnits))
}
// parse and handle network settings
def_net_type, arg_set = d.GetOk("def_net_type")
if arg_set {
ulr_values.Add("def_net", def_net_type.(string))
} }
// pass externalnetworkid if set
arg_value, arg_set = d.GetOk("extnet_id") ipcidr, arg_set = d.GetOk("ipcidr")
if arg_set {
ulr_values.Add("ipcidr", ipcidr.(string))
}
ext_net_id, arg_set = d.GetOk("ext_net_id")
if arg_set { if arg_set {
url_values.Add("extNetId", fmt.Sprintf("%d", arg_value)) ulr_values.Add("extNetId", ext_net_id.(int))
}
ext_ip, arg_set = d.GetOk("ext_ip")
if arg_set {
ulr_values.Add("extIp", ext_ip.(string))
} }
api_resp, err := controller.decortAPICall("POST", ResgroupCreateAPI, url_values) api_resp, err := controller.decortAPICall("POST", ResgroupCreateAPI, url_values)
@ -120,6 +142,7 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
d.SetId(api_resp) // rg/create API returns ID of the newly creted resource group on success d.SetId(api_resp) // rg/create API returns ID of the newly creted resource group on success
rg.ID, _ = strconv.Atoi(api_resp) rg.ID, _ = strconv.Atoi(api_resp)
// re-read newly created RG to make sure schema contains complete and up to date set of specifications
return resourceResgroupRead(d, m) return resourceResgroupRead(d, m)
} }
@ -138,66 +161,81 @@ func resourceResgroupRead(d *schema.ResourceData, m interface{}) error {
} }
func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error { func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
// this method will only update quotas, if any are set
log.Debugf("resourceResgroupUpdate: called for RG name %q, account name %q", log.Debugf("resourceResgroupUpdate: called for RG name %q, account name %q",
d.Get("name").(string), d.Get("account").(string)) d.Get("name").(string), d.Get("account").(string))
quota_value, arg_set := d.GetOk("quotas") do_update := false
if !arg_set {
// if there are no quotas set explicitly in the resource configuration - no change will be done
log.Debugf("resourceResgroupUpdate: quotas are not set in the resource config - no update on this resource will be done")
return resourceResgroupRead(d, m)
}
quotaconfig_new, _ := makeQuotaConfig(quota_value.([]interface{}))
quota_value, _ = d.GetChange("quotas") // returns old as 1st, new as 2nd argument
quotaconfig_old, _ := makeQuotaConfig(quota_value.([]interface{}))
controller := m.(*ControllerCfg) controller := m.(*ControllerCfg)
url_values := &url.Values{} url_values := &url.Values{}
url_values.Add("cloudspaceId", d.Id()) url_values.Add("rgId", d.Id())
url_values.Add("name", d.Get("name").(string))
name_new, name_set := d.GetOk("name")
do_update := false if name_set {
log.Debugf("resourceResgroupUpdate: name specified - looking for deltas from the old settings.")
if quotaconfig_new.Cpu != quotaconfig_old.Cpu { name_old, _ := d.GetChange("name")
do_update = true if name_old.(string) != name_new.(string) {
log.Debugf("resourceResgroupUpdate: Cpu diff %d <- %d", quotaconfig_new.Cpu, quotaconfig_old.Cpu) do_update := true
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quotaconfig_new.Cpu)) url_values.Add("name", name_new.(string))
} }
if quotaconfig_new.Disk != quotaconfig_old.Disk {
do_update = true
log.Debugf("resourceResgroupUpdate: Disk diff %d <- %d", quotaconfig_new.Disk, quotaconfig_old.Disk)
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotaconfig_new.Disk))
}
if quotaconfig_new.Ram != quotaconfig_old.Ram {
do_update = true
log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotaconfig_new.Ram, quotaconfig_old.Ram)
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotaconfig_new.Ram))
} }
if quotaconfig_new.NetTraffic != quotaconfig_old.NetTraffic { quota_value, quota_set := d.GetOk("quota")
do_update = true if quota_set {
log.Debugf("resourceResgroupUpdate: NetTraffic diff %d <- %d", quotaconfig_new.NetTraffic, quotaconfig_old.NetTraffic) log.Debugf("resourceResgroupUpdate: quota specified - looking for deltas from the old quota.")
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quotaconfig_new.NetTraffic)) quotarecord_new, _ := makeQuotaRecord(quota_value.([]interface{}))
quota_value_old, _ = d.GetChange("quota") // returns old as 1st, new as 2nd return value
quotarecord_old, _ := makeQuotaRecord(quota_value_old.([]interface{}))
if quotarecord_new.Cpu != quotarecord_old.Cpu {
do_update = true
log.Debugf("resourceResgroupUpdate: Cpu diff %d <- %d", quotarecord_new.Cpu, quotarecord_old.Cpu)
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quotarecord_new.Cpu))
}
if quotarecord_new.Disk != quotarecord_old.Disk {
do_update = true
log.Debugf("resourceResgroupUpdate: Disk diff %d <- %d", quotarecord_new.Disk, quotarecord_old.Disk)
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotarecord_new.Disk))
}
if quotarecord_new.Ram != quotarecord_old.Ram {
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))
}
if quotarecord_new.ExtTraffic != quotarecord_old.ExtTraffic {
do_update = true
log.Debugf("resourceResgroupUpdate: NetTraffic diff %d <- %d", quotarecord_new.ExtTraffic, quotarecord_old.ExtTraffic)
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quotarecord_new.NetTraffic))
}
if quotarecord_new.ExtIPs != quotarecord_old.ExtIPs {
do_update = true
log.Debugf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotarecord_new.ExtIPs, quotarecord_old.ExtIPs)
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quotarecord_new.ExtIPs))
}
} }
if quotaconfig_new.ExtIPs != quotaconfig_old.ExtIPs { desc_new, desc_set := d.GetOk("desc")
do_update = true if desc_set {
log.Debugf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotaconfig_new.ExtIPs, quotaconfig_old.ExtIPs) log.Debugf("resourceResgroupUpdate: description specified - looking for deltas from the old settings.")
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quotaconfig_new.ExtIPs)) desc_old, _ := d.GetChange("desc")
if desc_old.(string) != desc_new.(string) {
do_update := true
url_values.Add("desc", desc_new.(string))
}
} }
if do_update { if do_update {
log.Debugf("resourceResgroupUpdate: some new quotas are set - updating the resource") log.Debugf("resourceResgroupUpdate: detected delta between new and old RG specs - updating the RG")
_, err := controller.decortAPICall("POST", ResgroupUpdateAPI, url_values) _, err := controller.decortAPICall("POST", ResgroupUpdateAPI, url_values)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
log.Debugf("resourceResgroupUpdate: no difference in quotas between old and new state - no update on this resource will be done") log.Debugf("resourceResgroupUpdate: no difference between old and new state - no update on the RG will be done")
} }
return resourceResgroupRead(d, m) return resourceResgroupRead(d, m)
@ -216,14 +254,14 @@ func resourceResgroupDelete(d *schema.ResourceData, m interface{}) error {
return nil return nil
} }
params := &url.Values{} url_values := &url.Values{}
params.Add("rgId", d.Id()) url_values.Add("rgId", d.Id())
params.Add("force", "true") url_values.Add("force", "true")
params.Add("permanently", "true") url_values.Add("permanently", "true")
params.Add("reason", "Destroyed by DECORT Terraform provider") url_values.Add("reason", "Destroyed by DECORT Terraform provider")
controller := m.(*ControllerCfg) controller := m.(*ControllerCfg)
vm_facts, err = controller.decortAPICall("POST", ResgroupDeleteAPI, params) _, err = controller.decortAPICall("POST", ResgroupDeleteAPI, url_values)
if err != nil { if err != nil {
return err return err
} }
@ -232,7 +270,7 @@ func resourceResgroupDelete(d *schema.ResourceData, m interface{}) error {
} }
func resourceResgroupExists(d *schema.ResourceData, m interface{}) (bool, error) { func resourceResgroupExists(d *schema.ResourceData, m interface{}) (bool, error) {
// Reminder: according to Terraform rules, this function should not modify ResourceData argument // Reminder: according to Terraform rules, this function should NOT modify ResourceData argument
rg_facts, err := utilityResgroupCheckPresence(d, m) rg_facts, err := utilityResgroupCheckPresence(d, m)
if rg_facts == "" { if rg_facts == "" {
if err != nil { if err != nil {
@ -274,10 +312,30 @@ func resourceResgroup() *schema.Resource {
Description: "Name of the account, which this resource group belongs to.", Description: "Name of the account, which this resource group belongs to.",
}, },
"extnet_id": &schema.Schema { "def_net": &schema.Schema {
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.",
},
"ipcidr": &schema.Schema {
Type: schema.TypeString,
Optional: true,
Description: "Address of the netowrk inside the private network segment (aka ViNS) if def_net=PRIVATE",
},
"ext_net_id": &schema.Schema {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
Description: "ID of the external network, which this resource group will be connected to by default.", Default: 0,
Description: "ID of the external network, which this resource group will use as default for its computes if def_net=PUBLIC",
},
"ext_ip": &schema.Schema {
Type: schema.TypeString,
Optional: true,
Description: "IP address on the external netowrk to request, if def_net=PUBLIC",
}, },
"account_id": &schema.Schema { "account_id": &schema.Schema {
@ -292,14 +350,51 @@ func resourceResgroup() *schema.Resource {
Description: "Unique ID of the grid, where this resource group is deployed.", Description: "Unique ID of the grid, where this resource group is deployed.",
}, },
"quotas": { "quota": {
Type: schema.TypeList, Type: schema.TypeList,
Optional: true, Optional: true,
MaxItems: 1, MaxItems: 1,
Elem: &schema.Resource { Elem: &schema.Resource {
Schema: quotasSubresourceSchema(), Schema: quotasSubresourceSchema(),
}, },
Description: "Quotas on the resources for this resource group.", Description: "Quota settings for this resource group.",
},
"desc": {
Type: schema.TypeString,
Optional: true,
Description: "User-defined text description of this resource group."
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current status of this resource group.",
},
"def_net_id": &schema.Schema {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the default network for this resource group (if any).",
},
"vins": {
Type: schema.TypeList,
Computed: true,
MaxItems: LimitMaxVinsPerResgroup,
Elem: &schema.Resource {
Schema: vinsRgSubresourceSchema() // this is a list of ints
},
Description: "List of VINs deployed in this resource group.",
},
"computes": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource {
Schema: computesRgSubresourceSchema() //this is a list of ints
},
Description: "List of computes deployed in this resource group."
}, },
}, },
} }

@ -68,6 +68,8 @@ func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupGetResp,
return model, nil return model, nil
} }
// On success this function returns a string, as returned by API rg/get, which could be unmarshalled
// into ResgroupGetResp structure
func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string, error) { func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
// This function tries to locate resource group by its name and account name. // This function tries to locate resource group by its name and account name.
// If succeeded, it returns non empty string that contains JSON formatted facts about the // If succeeded, it returns non empty string that contains JSON formatted facts about the
@ -79,7 +81,7 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
// .../rg/list API with includedeleted=false // .../rg/list API with includedeleted=false
// //
// This function does not modify its ResourceData argument, so it is safe to use it as core // This function does not modify its ResourceData argument, so it is safe to use it as core
// method for the resource's Exists method. // method for the Terraform resource Exists method.
// //
name := d.Get("name").(string) name := d.Get("name").(string)
account_name := d.Get("account").(string) account_name := d.Get("account").(string)
@ -94,7 +96,7 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
log.Debugf("%s", body_string) log.Debugf("%s", body_string)
log.Debugf("utilityResgroupCheckPresence: ready to decode response body from %q", ResgroupListAPI) log.Debugf("utilityResgroupCheckPresence: ready to decode response body from %q", ResgroupListAPI)
model := CloudspacesListResp{} model := ResgroupListResp{}
err = json.Unmarshal([]byte(body_string), &model) err = json.Unmarshal([]byte(body_string), &model)
if err != nil { if err != nil {
return "", err return "", err
@ -148,5 +150,5 @@ func utilityGetAccountIdByName(account_name string, m interface{}) (int, error)
} }
} }
return 0, fmt.Errorf("Cannot find account %q for the current user. Check account value and your access rights", account_name) return 0, fmt.Errorf("Cannot find account %q for the current user. Check account name and your access rights", account_name)
} }
Loading…
Cancel
Save