Add account handler and refine other code. No testing yet!

rc-1.0
Sergey Shubin svs1370 4 years ago
parent 92528adf2b
commit 2f4be0b92a

@ -0,0 +1,136 @@
/*
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/
package decort
import (
"encoding/json"
"fmt"
"log"
// "net/url"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
)
func flattenAccount(d *schema.ResourceData, acc_facts string) error {
// NOTE: this function modifies ResourceData argument - as such it should never be called
// from resourceAccountExists(...) method
// log.Debugf("flattenAccount: ready to decode response body from %q", CloudspacesGetAPI)
details := AccountRecord{}
err := json.Unmarshal([]byte(rg_facts), &details)
if err != nil {
return err
}
log.Debugf("flattenAccount: decoded Account name %q / ID %d, status %q",
details.Name, details.ID, details.Status)
d.SetId(fmt.Sprintf("%d", details.ID))
d.Set("name", details.Name)
d.Set("status", details.Status)
return nil
}
func dataSourceAccountRead(d *schema.ResourceData, m interface{}) error {
acc_facts, err := utilityAccountCheckPresence(d, m)
if acc_facts == "" {
// if empty string is returned from utilityAccountCheckPresence then there is no
// such account and err tells so - just return it to the calling party
d.SetId("") // ensure ID is empty in this case
return err
}
return flattenAccount(d, acc_facts)
}
func dataSourceAccount() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
Read: dataSourceAccountRead,
Timeouts: &schema.ResourceTimeout{
Read: &Timeout30s,
Default: &Timeout60s,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of the account. Names are case sensitive and unique.",
},
"account_id": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: "Unique ID of the account. If account ID is specified, then account name is ignored.",
},
"status": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "Current status of the account."
}
/* We keep the following attributes commented out, as we are not implementing account
management with Terraform plugin, so we do not need this extra info.
"quota": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: quotaRgSubresourceSchema(), // this is a dictionary
},
Description: "Quotas on the resources for this account and all its resource groups.",
},
"resource_groups": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema {
Type: schema.TypeInt,
},
Description: "IDs of resource groups in this account."
},
"vins": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema {
Type: schema.TypeInt,
},
Description: "IDs of VINSes created at the account level."
},
*/
},
},
}
}

@ -0,0 +1,153 @@
/*
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/
package decort
import (
"encoding/json"
"fmt"
"log"
"net/url"
// "strconv"
"github.com/hashicorp/terraform/helper/schema"
// "github.com/hashicorp/terraform/helper/validation"
)
func utilityAccountCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
controller := m.(*ControllerCfg)
url_values := &url.Values{}
acc_id, arg_set := d.GetOk("account_id")
if arg_set {
// get Account right away by its ID
log.Debugf("utilityAccountCheckPresence: locating Account by its ID %d", acc_id.(int))
url_values.Add("accountId", fmt.Sprintf("%d", acc_id.(int)))
api_resp, err := controller.decortAPICall("POST", AccountsGetAPI, url_values)
if err != nil {
return "", err
}
return api_resp, nil
}
acc_name, arg_set := d.GetOk("name")
if !arg_set {
// neither ID nor name - no account for you!
return "", fmt.Error("Cannot check account presence if name is empty and no account ID specified.")
}
api_resp, err := controller.decortAPICall("POST", AccountsListAPI, url_values)
if err != nil {
return "", err
}
// log.Debugf("%s", api_resp)
// log.Debugf("utilityAccountCheckPresence: ready to decode response body from %q", AccountsListAPI)
acc_list := AccountsListResp{}
err = json.Unmarshal([]byte(api_resp), &acc_list)
if err != nil {
return "", err
}
log.Debugf("utilityAccountCheckPresence: traversing decoded Json of length %d", len(model))
for index, item := range acc_list {
// match by account name
if item.Name == acc_name.(string) {
log.Debugf("utilityAccountCheckPresence: match account name %q / ID %d at index %d",
item.Name, item.ID, index)
// NB: unlike accounts/get API, accounts/list API returns abridged set of account info,
// for instance it does not return quotas
reencoded_item, err := json.Marshal(item)
if err != nil {
return "", err
}
return reencoded_item.(string), nil
}
}
return "", fmt.Errorf("Cannot find account name %q owned by account ID %d", name, validated_account_id)
}
func utilityGetAccountIdBySchema(d *schema.ResourceData, m interface{}) (int, error) {
/*
This function expects schema that contains the following two elements:
"account_name": &schema.Schema{
Type: schema.TypeString,
Required: Optional,
Description: "Name of the account, ....",
},
"account_id": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: "Unique ID of the account, ....",
},
Then it will check, which argument is set, and if account name is present, it will
initiate API calls to the DECORT cloud controller and try to match relevant account
by the name.
*/
account_id, arg_set := d.GetOk("account_id")
if arg_set {
if account_id.(int) > 0 {
return account_id.(int), nil
}
return 0, fmt.Error("Account ID must be positive, if set.")
}
account_name, arg_set := d.GetOk("account_name")
if !arg_set {
return 0, fmt.Error("Non-empty account name or positive account ID must be specified.")
}
controller := m.(*ControllerCfg)
url_values := &url.Values{}
body_string, err := controller.decortAPICall("POST", AccountsListAPI, url_values)
if err != nil {
return 0, err
}
model := AccountsListResp{}
err = json.Unmarshal([]byte(body_string), &model)
if err != nil {
return 0, err
}
log.Debugf("utilityGetAccountIdBySchema: traversing decoded Json of length %d", len(model))
for index, item := range model {
// need to match Account by name
if item.Name == account_name.(string) {
log.Debugf("utilityGetAccountIdBySchema: match Account name %q / ID %d at index %d",
item.Name, item.ID, index)
return item.ID, nil
}
}
return 0, fmt.Errorf("Cannot find account %q for the current user. Check account name and your access rights", account_name.(string))
}

@ -49,8 +49,20 @@ func parseComputeDisks(disks []DiskRecord) []interface{} {
for i, value := range disks { for i, value := range disks {
// keys in this map should correspond to the Schema definition // keys in this map should correspond to the Schema definition
// as returned by dataSourceDiskSchemaMake() // as returned by dataSourceDiskSchemaMake()
elem[" attribute "] = value. attribute elem["name"] = value.Name
... elem["disk_id"] = value.ID
elem["account_id"] = value.AccountID
elem["account_name"] = value.AccountName
elem["description"] = value.Desc
elem["image_id"] = value.ImageID
elem["size"] = value.SizeMax
elem["type"] = value.Type
elem["sep_id"] = value.SepID
elem["sep_type"] = value.SepType
elem["pool"] = value.Pool
elem["status"] = value.Status
elem["tech_status"] = value.TechStatus
elem["compute_id"] = value.ComputeID
result[i] = elem result[i] = elem
} }
@ -71,8 +83,22 @@ func parseComputeInterfaces(ifaces []InterfaceRecord) []interface{} {
for i, value := range ifaces { for i, value := range ifaces {
// Keys in this map should correspond to the Schema definition // Keys in this map should correspond to the Schema definition
// as returned by dataSourceInterfaceSchemaMake() // as returned by dataSourceInterfaceSchemaMake()
elem[" attribute "] = value. attribute elem["net_id"] = value.NetId
... elem["net_type"] = value.NetType
elem["ip_address"] = value.IPAddress
elem["netmask"] = value.NetMask
elem["mac"] = value.MAC
elem["default_gw"] = value.DefaultGW
elem["name"] = value.Name
elem["connection_id"] = value.ConnID
elem["connection_type"] = value.ConnType
qos_schema := interfaceQosSubresourceSchemaMake()
qos_schema.Set("egress_rate", value.QOS.ERate)
qos_schema.Set("ingress_rate", value.QOS.InRate)
qos_schema.Set("ingress_burst", value.QOS.InBurst)
elem["qos"] = qos_schema
result[i] = elem result[i] = elem
} }
@ -93,7 +119,7 @@ func flattenCompute(d *schema.ResourceData, comp_facts string) error {
return err return err
} }
log.Debugf("flattenCompute: model.ID %d, model.ResGroupID %d", model.ID, model.ResGroupID) log.Debugf("flattenCompute: ID %d, ResGroupID %d", model.ID, model.ResGroupID)
d.SetId(fmt.Sprintf("%d", model.ID)) d.SetId(fmt.Sprintf("%d", model.ID))
d.Set("compute_id", model.ID) d.Set("compute_id", model.ID)
@ -119,25 +145,15 @@ func flattenCompute(d *schema.ResourceData, comp_facts string) error {
} }
if len(model.Interfaces) > 0 { if len(model.Interfaces) > 0 {
log.Printf("flattenCompute: calling parseComputeInterfaces for %d interfaces", len(model.Interfaces)) 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("interfaces", parseComputeInterfaces(model.Interfaces)); err != nil {
return err return err
} }
} }
if len(model.GuestLogins) > 0 { if len(model.GuestLogins) > 0 {
log.Printf("flattenCompute: calling parseGuestLogins") log.Debugf("flattenCompute: calling parseGuestLogins for %d logins", len(model.GuestLogins))
guest_logins := parseGuestLogins(model.GuestLogins) if err = d.Set("guest_logins", parseGuestLogins(model.GuestLogins)); err != nil {
if err = d.Set("guest_logins", guest_logins); err != nil {
return err
}
default_login := guest_logins[0].(map[string]interface{})
// set user & password attributes to the corresponding values of the 1st item in the list
if err = d.Set("user", default_login["login"]); err != nil {
return err
}
if err = d.Set("password", default_login["password"]); err != nil {
return err return err
} }
} }
@ -263,7 +279,7 @@ func dataSourceCompute() *schema.Resource {
Type: schema.TypeList, Type: schema.TypeList,
Computed: true, Computed: true,
Elem: &schema.Resource { Elem: &schema.Resource {
Schema: interfaceSubresourceSchema(), Schema: interfaceSubresourceSchemaMake(),
}, },
Description: "Specification for the virtual NICs configured on this compute instance.", Description: "Specification for the virtual NICs configured on this compute instance.",
}, },

@ -74,15 +74,14 @@ func utilityDiskCheckPresence(d *schema.ResourceData, m interface{}) (string, er
return "", fmt.Error("Cannot locate disk if name is empty and no disk ID specified.") return "", fmt.Error("Cannot locate disk if name is empty and no disk ID specified.")
} }
account_id, acc_id_set := d.GetOk("account_id") // Valid account ID is required to locate disks
if !acc_id_set { // obtain Account ID by account name - it should not be zero on success
account_name, arg_set := d.GetOkd("account_name") validated_account_id, err := utilityGetAccountIdBySchema(d, m)
if !arg_set { if err != nil {
return "", fmt.Error("Cannot locate disk by name %s if neither account ID nor account name are set", disk_name.(string)) return err
}
} }
url_values.Add("accountId", fmt.Sprintf("%d", account_id.(int))) url_values.Add("accountId", fmt.Sprintf("%d", validated_account_id))
disk_facts, err := controller.decortAPICall("POST", DisksListAPI, url_values) disk_facts, err := controller.decortAPICall("POST", DisksListAPI, url_values)
if err != nil { if err != nil {
return "", err return "", err
@ -100,7 +99,7 @@ func utilityDiskCheckPresence(d *schema.ResourceData, m interface{}) (string, er
log.Debugf("utilityDiskCheckPresence: traversing decoded JSON of length %d", len(disks_list)) log.Debugf("utilityDiskCheckPresence: traversing decoded JSON of length %d", len(disks_list))
for _, item := range disks_list { for _, item := range disks_list {
// need to match disk by name, return the first match // need to match disk by name, return the first match
if item.Name == disk_name && item.Status != "DESTROYED" { if item.Name == disk_name.(string) && item.Status != "DESTROYED" {
log.Printf("utilityDiskCheckPresence: index %d, matched disk name %q", index, item.Name) log.Printf("utilityDiskCheckPresence: index %d, matched disk name %q", index, item.Name)
// we found the disk we need - not get detailed information via API call to disks/get // we found the disk we need - not get detailed information via API call to disks/get
/* /*

@ -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");
@ -16,10 +16,10 @@ limitations under the License.
*/ */
/* /*
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
Technology platfom. Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/ */
package decort package decort
@ -34,39 +34,41 @@ import (
"github.com/hashicorp/terraform/helper/validation" "github.com/hashicorp/terraform/helper/validation"
) )
func dataSourceImageRead(d *schema.ResourceData, m interface{}) error { func dataSourceImageRead(d *schema.ResourceData, m interface{}) error {
name := d.Get("name").(string) name := d.Get("name").(string)
rgid, rgid_set := d.GetOk("rgid") // rg_id, rgid_set := d.GetOk("rg_id")
tenant_id, tenant_set := d.GetOk("tenant_id") account_id, account_set := d.GetOk("account_id")
controller := m.(*ControllerCfg) controller := m.(*ControllerCfg)
url_values := &url.Values{} url_values := &url.Values{}
if tenant_set { if account_set {
url_values.Add("accountId", fmt.Sprintf("%d",tenant_id.(int))) url_values.Add("accountId", fmt.Sprintf("%d", account_id.(int)))
}
if rgid_set {
url_values.Add("cloudspaceId", fmt.Sprintf("%d",rgid.(int)))
} }
body_string, err := controller.decortAPICall("POST", ImagesListAPI, url_values) body_string, err := controller.decortAPICall("POST", ImagesListAPI, url_values)
if err != nil { if err != nil {
return err return err
} }
log.Printf("dataSourceImageRead: ready to decode response body") log.Debugf("dataSourceImageRead: ready to decode response body from %q", ImagesListAPI)
model := ImagesListResp{} model := ImagesListResp{}
err = json.Unmarshal([]byte(body_string), &model) err = json.Unmarshal([]byte(body_string), &model)
if err != nil { if err != nil {
return err return err
} }
log.Printf("%#v", model) // log.Printf("%#v", model)
log.Printf("dataSourceImageRead: traversing decoded JSON of length %d", len(model)) log.Debugf("dataSourceImageRead: traversing decoded JSON of length %d", len(model))
for index, item := range model { for index, item := range model {
// need to match VM by name // need to match Image by name
if item.Name == name { if item.Name == name {
log.Printf("dataSourceImageRead: index %d, matched name %q", index, item.Name) log.Printf("dataSourceImageRead: index %d, matched name %q", index, item.Name)
d.SetId(fmt.Sprintf("%d", model[index].ID)) d.SetId(fmt.Sprintf("%d", item.ID))
d.Set("account_id", item.AccountID)
d.Set("arch", item.Arch)
d.Set("sep_id", item.SepID)
d.Set("pool", item.Pool)
d.Set("status", item.Status)
d.Set("size", item.Size)
// d.Set("field_name", value) // d.Set("field_name", value)
return nil return nil
} }
@ -76,36 +78,67 @@ func dataSourceImageRead(d *schema.ResourceData, m interface{}) error {
} }
func dataSourceImage() *schema.Resource { func dataSourceImage() *schema.Resource {
return &schema.Resource { return &schema.Resource{
SchemaVersion: 1, SchemaVersion: 1,
Read: dataSourceImageRead, Read: dataSourceImageRead,
Timeouts: &schema.ResourceTimeout { Timeouts: &schema.ResourceTimeout{
Read: &Timeout30s, Read: &Timeout30s,
Default: &Timeout60s, Default: &Timeout60s,
}, },
Schema: map[string]*schema.Schema { Schema: map[string]*schema.Schema{
"name": { "name": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
Description: "Name of the OS image to locate. This parameter is case sensitive.", Description: "Name of the OS image to locate. This parameter is case sensitive.",
}, },
"tenant_id": { "account_id": {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
ValidateFunc: validation.IntAtLeast(1), ValidateFunc: validation.IntAtLeast(1),
Description: "ID of the tenant to limit image search to.", Description: "Optional ID of the account to limit image search to.",
}, },
"rgid": { "arch": {
Type: schema.TypeInt, Type: schema.TypeString,
Optional: true, Computed: true,
ValidateFunc: validation.IntAtLeast(1), Description: "Binary architecture this image is created for.",
Description: "ID of the resource group to limit image search to.", },
"sep_id": {
Type: schema.TypeString,
Computed: true,
Description: "Storage end-point provider serving this image.",
},
/*
"sep_type": {
Type: schema.TypeString,
Computed: true,
Description: "Type of the storage end-point provider serving this image.",
},
*/
"pool": {
Type: schema.TypeString,
Computed: true,
Description: "Pool where this image is located.",
},
"size": {
Type: schema.TypeInt,
Computed: true,
Description: "Size of the image in GB.",
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current model status of this disk.",
}, },
}, },
} }
} }

@ -0,0 +1,330 @@
/*
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This file contains definitions and code for handling Interface component of Compute schema
*/
package decort
import (
"log"
"strconv"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
)
func interfaceSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"net_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the network entity this interface is connected to.",
},
"net_type": {
Type: schema.TypeString,
Computed: true,
Description: "Type of the network entity this interface is connected to.",
},
"ip_address": {
Type: schema.TypeString,
Computed: true,
Description: "IP addresses assigned to this interface.",
},
"netmask": {
Type: schema.TypeInt,
Computed: true,
Description: "Network mask to be used with this interface.",
},
"mac": {
Type: schema.TypeString,
Computed: true,
Description: "MAC address of this interface.",
},
"default_gw": {
Type: schema.TypeString,
Computed: true,
Description: "Default gateway associated with this interface.",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "Interface name.",
},
"connection_id": {
Type: schema.TypeInt,
Computed: true,
Description: "VxLAN or VLAN ID this interface is connected to.",
},
"connection_type": {
Type: schema.TypeString,
Computed: true,
Description: "Type of the segment (VLAN or VxLAN) this interface is connected to.",
},
"qos": {
Computed: true,
Elem: &schema.Resource{
Schema: interfaceQosSubresourceSchemaMake(),
},
Description: "Details about the guest OS users provisioned together with this compute instance.",
},
}
return rets
}
func interfaceQosSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"egress_rate": {
Type: schema.TypeInt,
Computed: true,
Description: "Egress rate limit on this interface.",
},
"ingress_burst": {
Type: schema.TypeInt,
Computed: true,
Description: "Ingress burst limit on this interface.",
},
"ingress_rate": {
Type: schema.TypeInt,
Computed: true,
Description: "Ingress rate limit on this interface.",
},
"guid": {
Type: schema.TypeString,
Computed: true,
Description: "GUID of this QoS record.",
},
}
return rets
}
/*
func flattenNetworks(nets []NicRecord) []interface{} {
// this function expects an array of NicRecord as returned by machines/get API call
// NOTE: it does NOT expect a strucutre as returned by externalnetwork/list
var length = 0
var strarray []string
for _, value := range nets {
if value.NicType == "PUBLIC" {
length += 1
}
}
log.Printf("flattenNetworks: found %d NICs with PUBLIC type", length)
result := make([]interface{}, length)
if length == 0 {
return result
}
elem := make(map[string]interface{})
var subindex = 0
for index, value := range nets {
if value.NicType == "PUBLIC" {
// this will be changed as network segments entity
// value.Params for ext net comes in a form "gateway:176.118.165.1 externalnetworkId:6"
// for network_id we need to extract from this string
strarray = strings.Split(value.Params, " ")
substr := strings.Split(strarray[1], ":")
elem["network_id"], _ = strconv.Atoi(substr[1])
elem["ip_range"] = value.IPAddress
// elem["label"] = ... - should be uncommented for the future release
log.Printf("flattenNetworks: parsed element %d - network_id %d, ip_range %q",
index, elem["network_id"].(int), value.IPAddress)
result[subindex] = elem
subindex += 1
}
}
return result
}
func makePortforwardsConfig(arg_list []interface{}) (pfws []PortforwardConfig, count int) {
count = len(arg_list)
if count < 1 {
return nil, 0
}
pfws = make([]PortforwardConfig, count)
var subres_data map[string]interface{}
for index, value := range arg_list {
subres_data = value.(map[string]interface{})
// pfws[index].Label = subres_data["label"].(string) - should be uncommented for future release
pfws[index].ExtPort = subres_data["ext_port"].(int)
pfws[index].IntPort = subres_data["int_port"].(int)
pfws[index].Proto = subres_data["proto"].(string)
}
return pfws, count
}
func flattenPortforwards(pfws []PortforwardRecord) []interface{} {
result := make([]interface{}, len(pfws))
elem := make(map[string]interface{})
var port_num int
for index, value := range pfws {
// elem["label"] = ... - should be uncommented for the future release
// external port field is of TypeInt in the portforwardSubresourceSchema, but string is returned
// by portforwards/list API, so we need conversion here
port_num, _ = strconv.Atoi(value.ExtPort)
elem["ext_port"] = port_num
// internal port field is of TypeInt in the portforwardSubresourceSchema, but string is returned
// by portforwards/list API, so we need conversion here
port_num, _ = strconv.Atoi(value.IntPort)
elem["int_port"] = port_num
elem["proto"] = value.Proto
elem["ext_ip"] = value.ExtIP
elem["int_ip"] = value.IntIP
result[index] = elem
}
return result
}
func portforwardSubresourceSchema() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
/* this should be uncommented for the future release
"label": {
Type: schema.TypeString,
Required: true,
Description: "Unique label of this network connection to identify it amnong other connections for this VM.",
},
*/
"ext_port": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(1, 65535),
Description: "External port number for this port forwarding rule.",
},
"int_port": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(1, 65535),
Description: "Internal port number for this port forwarding rule.",
},
"proto": {
Type: schema.TypeString,
Required: true,
// ValidateFunc: validation.IntBetween(1, ),
Description: "Protocol type for this port forwarding rule. Should be either 'tcp' or 'udp'.",
},
"ext_ip": {
Type: schema.TypeString,
Computed: true,
Description: ".",
},
"int_ip": {
Type: schema.TypeString,
Computed: true,
Description: ".",
},
}
return rets
}
func flattenNICs(nics []NicRecord) []interface{} {
var result = make([]interface{}, len(nics))
elem := make(map[string]interface{})
for index, value := range nics {
elem["status"] = value.Status
elem["type"] = value.NicType
elem["mac"] = value.MacAddress
elem["ip_address"] = value.IPAddress
elem["parameters"] = value.Params
elem["reference_id"] = value.ReferenceID
elem["network_id"] = value.NetworkID
result[index] = elem
}
return result
}
func nicSubresourceSchema() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current status of this NIC.",
},
"type": {
Type: schema.TypeString,
Computed: true,
Description: "Type of this NIC.",
},
"mac": {
Type: schema.TypeString,
Computed: true,
Description: "MAC address assigned to this NIC.",
},
"ip_address": {
Type: schema.TypeString,
Computed: true,
Description: "IP address assigned to this NIC.",
},
"parameters": {
Type: schema.TypeString,
Computed: true,
Description: "Additional NIC parameters.",
},
"reference_id": {
Type: schema.TypeString,
Computed: true,
Description: "Reference ID of this NIC.",
},
"network_id": {
Type: schema.TypeInt,
Computed: true,
Description: "Network ID which this NIC is connected to.",
},
}
return rets
}
*/

@ -15,17 +15,16 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package decs package decort
import ( import (
"log" "log"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
// "github.com/hashicorp/terraform/helper/validation" // "github.com/hashicorp/terraform/helper/validation"
) )
func flattenGuestLogins(logins []GuestLoginRecord) []interface{} { func parseGuestLogins(logins []OsUserRecord) []interface{} {
var result = make([]interface{}, len(logins)) var result = make([]interface{}, len(logins))
elem := make(map[string]interface{}) elem := make(map[string]interface{})
@ -34,28 +33,28 @@ func flattenGuestLogins(logins []GuestLoginRecord) []interface{} {
elem["guid"] = value.Guid elem["guid"] = value.Guid
elem["login"] = value.Login elem["login"] = value.Login
elem["password"] = value.Password elem["password"] = value.Password
elem["public_key"] = value.PubKey
result[index] = elem result[index] = elem
log.Printf("flattenGuestLogins: parsed element %d - login %q", log.Debugf("parseGuestLogins: parsed element %d - login %q", index, value.Login)
index, value.Login)
} }
return result return result
} }
func loginsSubresourceSchema() map[string]*schema.Schema { func loginsSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema { rets := map[string]*schema.Schema{
"guid": { "guid": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Default: "", Default: "",
Description: "GUID of this guest user.", Description: "GUID of this guest OS user.",
}, },
"login": { "login": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Default: "", Default: "",
Description: "Login name of this guest user.", Description: "Login name of this guest OS user.",
}, },
"password": { "password": {
@ -63,7 +62,14 @@ func loginsSubresourceSchema() map[string]*schema.Schema {
Optional: true, Optional: true,
Default: "", Default: "",
Sensitive: true, Sensitive: true,
Description: "Password of this guest user.", Description: "Password of this guest OS user.",
},
"public_key": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "SSH public key of this guest user.",
}, },
} }

@ -16,17 +16,15 @@ limitations under the License.
*/ */
/* /*
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
Technology platfom. Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/ */
package decort package decort
import ( import (
"time" "time"
) )
@ -40,157 +38,163 @@ var Timeout180s = time.Second * 180
// structures related to /cloudapi/rg/list API // structures related to /cloudapi/rg/list API
// //
type UserAclRecord struct { type UserAclRecord struct {
IsExplicit bool `json:"explicit"` IsExplicit bool `json:"explicit"`
Rights string `json:"right"` Rights string `json:"right"`
Status string `json:"status"` Status string `json:"status"`
Type string `json:"type"` Type string `json:"type"`
UgroupID string `json:"userGroupId"` UgroupID string `json:"userGroupId"`
// CanBeDeleted bool `json:"canBeDeleted"` // CanBeDeleted bool `json:"canBeDeleted"`
} }
type AccountAclRecord struct { type AccountAclRecord struct {
IsExplicit bool `json:"explicit"` IsExplicit bool `json:"explicit"`
Guid string `json:"guid"` Guid string `json:"guid"`
Rights string `json:"right"` Rights string `json:"right"`
Status string `json:"status"` Status string `json:"status"`
Type string `json:"type"` Type string `json:"type"`
UgroupID string `json:"userGroupId"` UgroupID string `json:"userGroupId"`
} }
type ResgroupRecord struct { type ResgroupRecord struct {
ACLs []UserAclRecord `json:"ACLs"` ACLs []UserAclRecord `json:"ACLs"`
Owner AccountAclRecord `json:"accountAcl"` Owner AccountAclRecord `json:"accountAcl"`
AccountID int `json:"accountId"` AccountID int `json:"accountId"`
AccountName string `json:"accountName"` AccountName string `json:"accountName"`
CreatedBy string `json:"createdBy"` CreatedBy string `json:"createdBy"`
CreatedTime uint64 `json:"createdTime"` CreatedTime uint64 `json:"createdTime"`
DefaultNetID int `json:"def_net_id"` DefaultNetID int `json:"def_net_id"`
DefaultNetType string `json:"def_net_type"` DefaultNetType string `json:"def_net_type"`
Decsription string `json:"desc"` Decsription string `json:"desc"`
GridID int `json:"gid"` GridID int `json:"gid"`
ID uint `json:"id"` ID uint `json:"id"`
LockStatus string `json:"lockStatus"` LockStatus string `json:"lockStatus"`
Name string `json:"name"` Name string `json:"name"`
Status string `json:"status"` Status string `json:"status"`
UpdatedBy string `json:"updatedBy"` UpdatedBy string `json:"updatedBy"`
UpdatedTime uint64 `json:"updatedTime"` UpdatedTime uint64 `json:"updatedTime"`
Vins []int `json:"vins"` Vins []int `json:"vins"`
Computes []int `json:"vms"` Computes []int `json:"vms"`
} }
const ResgroupListAPI = "/restmachine/cloudapi/rg/list" const ResgroupListAPI = "/restmachine/cloudapi/rg/list"
type ResgroupListResp []ResgroupRecord type ResgroupListResp []ResgroupRecord
// //
// structures related to /cloudapi/rg/create API call // structures related to /cloudapi/rg/create API call
// //
const ResgroupCreateAPI= "/restmachine/cloudapi/rg/create" const ResgroupCreateAPI = "/restmachine/cloudapi/rg/create"
// //
// structures related to /cloudapi/rg/update API call // structures related to /cloudapi/rg/update API call
// //
const ResgroupUpdateAPI= "/restmachine/cloudapi/rg/update" const ResgroupUpdateAPI = "/restmachine/cloudapi/rg/update"
type ResgroupUpdateParam struct { type ResgroupUpdateParam struct {
RgId int `json:"rgId"` RgId int `json:"rgId"`
Name string `json:"name"` Name string `json:"name"`
Desc string `json:"decs"` Desc string `json:"decs"`
Ram int `json:"maxMemoryCapacity"` Ram int `json:"maxMemoryCapacity"`
Disk int `json:"maxVDiskCapacity"` Disk int `json:"maxVDiskCapacity"`
Cpu int `json:"maxCPUCapacity"` Cpu int `json:"maxCPUCapacity"`
NetTraffic int `json:"maxNetworkPeerTransfer"` NetTraffic int `json:"maxNetworkPeerTransfer"`
Reason string `json:"reason"` Reason string `json:"reason"`
} }
// //
// structures related to /cloudapi/rg/get API call // structures related to /cloudapi/rg/get API call
// //
type QuotaRecord struct { // this is how quota is reported by /api/.../rg/get type QuotaRecord struct { // this is how quota is reported by /api/.../rg/get
Cpu int `json:"CU_C"` // CPU count in pcs Cpu int `json:"CU_C"` // CPU count in pcs
Ram int `json:"CU_M"` // RAM volume in MB Ram int `json:"CU_M"` // RAM volume in MB
Disk int `json:"CU_D"` // Disk capacity in GB Disk int `json:"CU_D"` // Disk capacity in GB
ExtIPs int `json:"CU_I"` // Ext IPs count ExtIPs int `json:"CU_I"` // Ext IPs count
ExtTraffic int `json:"CU_NP"` // Ext network traffic ExtTraffic int `json:"CU_NP"` // Ext network traffic
GpuUnits int `json:"gpu_units"` // GPU count GpuUnits int `json:"gpu_units"` // GPU count
} }
type ResourceRecord struct { // this is how actual usage is reported by /api/.../rg/get 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"`
ExtTraffic int `json:"exttraffic"` ExtTraffic int `json:"exttraffic"`
Gpu int `json:"gpu"` Gpu int `json:"gpu"`
Ram int `json:"ram"` Ram int `json:"ram"`
} }
type UsageRecord struct { type UsageRecord struct {
Current ResourceRecord `json:"Current"` Current ResourceRecord `json:"Current"`
Reserved ResourceRecord `json:"Reserved"` Reserved ResourceRecord `json:"Reserved"`
} }
const ResgroupGetAPI= "/restmachine/cloudapi/rg/get" const ResgroupGetAPI = "/restmachine/cloudapi/rg/get"
type ResgroupGetResp struct { type ResgroupGetResp struct {
ACLs []UserAclRecord `json:"ACLs"` ACLs []UserAclRecord `json:"ACLs"`
Usage UsageRecord `json:"Resources"` Usage UsageRecord `json:"Resources"`
AccountID int `json:"accountId"` AccountID int `json:"accountId"`
AccountName string `json:"accountName"` AccountName string `json:"accountName"`
CreatedBy string `json:"createdBy"` CreatedBy string `json:"createdBy"`
CreatedTime uint64 `json:"createdTime"` CreatedTime uint64 `json:"createdTime"`
DefaultNetID int `json:"def_net_id"` DefaultNetID int `json:"def_net_id"`
DefaultNetType string `json:"def_net_type"` DefaultNetType string `json:"def_net_type"`
DeletedBy string `json:"deletedBy"` DeletedBy string `json:"deletedBy"`
DeletedTime uint64 `json:"deletedTime"` DeletedTime uint64 `json:"deletedTime"`
Decsription string `json:"desc"` Decsription string `json:"desc"`
ID uint `json:"id"` ID uint `json:"id"`
LockStatus string `json:"lockStatus"` LockStatus string `json:"lockStatus"`
Name string `json:"name"` Name string `json:"name"`
Quota 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"`
Vins []int `json:"vins"` Vins []int `json:"vins"`
Computes []int `json:"vms"` Computes []int `json:"vms"`
Ignored map[string]interface{} `json:"-"` Ignored map[string]interface{} `json:"-"`
} }
// //
// structures related to /cloudapi/rg/update API // structures related to /cloudapi/rg/update API
// //
const ResgroupUpdateAPI = "/restmachine/cloudapi/rg/update" const ResgroupUpdateAPI = "/restmachine/cloudapi/rg/update"
type ResgroupUpdateParam struct { type ResgroupUpdateParam struct {
ID uint `json:"rgId"` ID uint `json:"rgId"`
Name string `json:"name"` Name string `json:"name"`
Decsription string `json:"desc"` Decsription string `json:"desc"`
Cpu int `json:"maxCPUCapacity"` Cpu int `json:"maxCPUCapacity"`
Ram int `json:"maxMemoryCapacity"` Ram int `json:"maxMemoryCapacity"`
Disk int `json:"maxVDiskCapacity"` Disk int `json:"maxVDiskCapacity"`
NetTraffic int `json:"maxNetworkPeerTransfer"` NetTraffic int `json:"maxNetworkPeerTransfer"`
ExtIPs int `json:"maxNumPublicIP"` ExtIPs int `json:"maxNumPublicIP"`
Reason string `json:"reason"` Reason string `json:"reason"`
} }
// //
// structures related to /cloudapi/rg/delete API // structures related to /cloudapi/rg/delete API
// //
const ResgroupDeleteAPI = "/restmachine/cloudapi/rg/delete" const ResgroupDeleteAPI = "/restmachine/cloudapi/rg/delete"
// //
// structures related to /cloudapi/rg/listComputes API // structures related to /cloudapi/rg/listComputes API
// //
type ComputeBriefRecord struct { // this is a brief compute specifiaction as returned by API rg/listComputes type ComputeBriefRecord struct { // this is a brief compute specifiaction as returned by API rg/listComputes
// we do not even include here all fields as returned by this API, but only the most important that // we do not even include here all fields as returned by this API, but only the most important that
// are really necessary to identify and distinguish computes // are really necessary to identify and distinguish computes
AccountID int `json:"accountId"` AccountID int `json:"accountId"`
AccountName string `json:"accountName"` AccountName string `json:"accountName"`
Name string `json:"name"` Name string `json:"name"`
ID uint `json:"id"` ID uint `json:"id"`
RgID int `json:"rgId"` RgID int `json:"rgId"`
RgName string `json:"rgName"` RgName string `json:"rgName"`
Status string `json:"status"` Status string `json:"status"`
TechStatus string `json:"techStatus"` TechStatus string `json:"techStatus"`
} }
const RgListComputesAPI = "/restmachine/cloudapi/rg/listComputes" const RgListComputesAPI = "/restmachine/cloudapi/rg/listComputes"
type RgListComputesResp []ComputeBriefRecord type RgListComputesResp []ComputeBriefRecord
// //
@ -198,339 +202,315 @@ type RgListComputesResp []ComputeBriefRecord
// //
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 KvmVmCreateParam struct { // this is unified structure for both x86 and PPC based KVM 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"`
Ram int `json:"ram"` Ram int `json:"ram"`
ImageID int `json:"imageId"` ImageID int `json:"imageId"`
BootDisk int `json:"bootDisk"` BootDisk int `json:"bootDisk"`
NetType string `json:"netType"` NetType string `json:"netType"`
NetId int `json:"netId"` NetId int `json:"netId"`
IPAddr string `json:"ipAddr"` IPAddr string `json:"ipAddr"`
UserData string `json:"userdata"` UserData string `json:"userdata"`
Description string `json:"desc"` Description string `json:"desc"`
Start bool `json:"start"` Start bool `json:"start"`
} }
// structures related to cloudapi/compute/delete API // structures related to cloudapi/compute/delete API
const ComputeDeleteAPI = "/restmachine/cloudapi/compute/delete" const ComputeDeleteAPI = "/restmachine/cloudapi/compute/delete"
// //
// structures related to /cloudapi/compute/list API // structures related to /cloudapi/compute/list API
// //
type InterfaceQosRecord struct {
ERate int `json:"eRate"`
Guid string `json:"guid"`
InBurst int `json:"inBurst"`
InRate int `json:"inRate"`
}
type InterfaceRecord struct { type InterfaceRecord struct {
ConnID int `json:"connId"` ConnID int `json:"connId"` // This is VLAN ID or VxLAN ID, depending on ConnType
ConnType string `json:"connType"` ConnType string `json:"connType"` // Either "VLAN" or "VXLAN" tag
DefaultGW string `json:"defGw"` DefaultGW string `json:"defGw"`
Guid string `json:"guid"` Guid string `json:"guid"`
IPAddress string `json:"ipAddress"` // without trailing network mask, i.e. "192.168.1.3" IPAddress string `json:"ipAddress"` // without trailing network mask, i.e. "192.168.1.3"
MAC string `json:"mac"` MAC string `json:"mac"`
Name string `json:"name"` Name string `json:"name"`
NetID int `json:"netId"` NetID int `json:"netId"` // This is either ExtNet ID or ViNS ID, depending on NetType
NetMaks int `json:"netMask"` NetMask int `json:"netMask"`
NetType string `json:"netType"` NetType string `json:"netType"` // Either "EXTNET" or "VINS" tag
PciSlot int `json:"pciSlot"` PciSlot int `json:"pciSlot"`
Target string `json:"target"` Target string `json:"target"`
Type string `json:"type"` Type string `json:"type"`
VNFs []int `json:"vnfs"` VNFs []int `json:"vnfs"`
QOS InterfaceQosRecord `json:"qos"`
} }
type SnapSetRecord struct { type SnapSetRecord struct {
Disks []int `json:"disks"` Disks []int `json:"disks"`
Guid string `json:"guid"` Guid string `json:"guid"`
Label string `json:"label"` Label string `json:"label"`
TimeStamp uint64 `json:"timestamp"` TimeStamp uint64 `json:"timestamp"`
} }
type ComputeRecord struct { type ComputeRecord struct {
AccountID int `json:"accountId"` AccountID int `json:"accountId"`
AccountName string `json:"accountName"` AccountName string `json:"accountName"`
ACLs []UserAclRecord `json:"acl"` ACLs []UserAclRecord `json:"acl"`
Arch string `json:"arch"` Arch string `json:"arch"`
BootDiskSize int `json:"bootdiskSize"` BootDiskSize int `json:"bootdiskSize"`
CloneReference int `json:"cloneReference"` CloneReference int `json:"cloneReference"`
Clones []int `json:"clones"` Clones []int `json:"clones"`
Cpus int `json:"cpus"` Cpus int `json:"cpus"`
CreatedBy string `json:"createdBy"` CreatedBy string `json:"createdBy"`
CreatedTime uint64 `json:"createdTime"` CreatedTime uint64 `json:"createdTime"`
DeletedBy string `json:"deletedBy"` DeletedBy string `json:"deletedBy"`
DeletedTime uint64 `json:"deletedTime"` DeletedTime uint64 `json:"deletedTime"`
Desc string `json:"desc"` Desc string `json:"desc"`
Disks []int `json:"disks"` Disks []int `json:"disks"`
GridID int `json:"gid"` GridID int `json:"gid"`
ID uint `json:"id"` ID uint `json:"id"`
ImageID int `json:"imageId"` ImageID int `json:"imageId"`
Interfaces []InterfaceRecord `json:"interfaces` Interfaces []InterfaceRecord `json:"interfaces`
LockStatus string `json:"lockStatus"` LockStatus string `json:"lockStatus"`
ManagerID int `json:"managerId"` ManagerID int `json:"managerId"`
Name string `json:"name"` Name string `json:"name"`
Ram int `json:"ram"` Ram int `json:"ram"`
RgID int `json:"rgId"` RgID int `json:"rgId"`
RgName string `json:"rgName"` RgName string `json:"rgName"`
SnapSets []SnapSetRecord `json:"snapSets"` SnapSets []SnapSetRecord `json:"snapSets"`
Status string `json:"status"` Status string `json:"status"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
TechStatus string `json:"techStatus"` TechStatus string `json:"techStatus"`
TotalDiskSize int `json:"totalDiskSize"` TotalDiskSize int `json:"totalDiskSize"`
UpdatedBy string `json:"updatedBy"` UpdatedBy string `json:"updatedBy"`
UpdateTime uint64 `json:"updateTime"` UpdateTime uint64 `json:"updateTime"`
UserManaged bool `json:"userManaged"` UserManaged bool `json:"userManaged"`
Vgpus []int `json:"vgpus"` Vgpus []int `json:"vgpus"`
VinsConnected int `json:"vinsConnected"` VinsConnected int `json:"vinsConnected"`
VirtualImageID int `json:"virtualImageId"` VirtualImageID int `json:"virtualImageId"`
} }
const ComputeListAPI = "/restmachine/cloudapi/compute/list" const ComputeListAPI = "/restmachine/cloudapi/compute/list"
type ComputeListResp []ComputeRecord type ComputeListResp []ComputeRecord
// //
// structures related to /cloudapi/compute/get // structures related to /cloudapi/compute/get
// //
type SnapshotRecord struct { type SnapshotRecord struct {
Guid string `json:"guid"` Guid string `json:"guid"`
Label string `json:"label"` Label string `json:"label"`
SnapSetGuid string `json:"snapSetGuid"` SnapSetGuid string `json:"snapSetGuid"`
SnapSetTime uint64 `json:"snapSetTime"` SnapSetTime uint64 `json:"snapSetTime"`
TimeStamp uint64 `json:"timestamp"` TimeStamp uint64 `json:"timestamp"`
} }
type DiskRecord struct { type DiskRecord struct {
// ACLs `json:"ACL"` - it is a dictionary, special parsing required // ACLs `json:"ACL"` - it is a dictionary, special parsing required
// was - Acl map[string]string `json:"acl"` // was - Acl map[string]string `json:"acl"`
AccountID int `json:"accountId"` 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"` BootPartition int `json:"bootPartition"`
CreatedTime uint64 `json:"creationTime"` CreatedTime uint64 `json:"creationTime"`
DeletedTime uint64 `json:"deletionTime"` DeletedTime uint64 `json:"deletionTime"`
Desc string `json:"descr"` Desc string `json:"descr"`
DestructionTime uint64 `json:"destructionTime"` DestructionTime uint64 `json:"destructionTime"`
DiskPath string `json:"diskPath"` DiskPath string `json:"diskPath"`
GridID int `json:"gid"` GridID int `json:"gid"`
ID uint `json:"id"` ID uint `json:"id"`
ImageID int `json:"imageId"` ImageID int `json:"imageId"`
Images []int `json:"images"` Images []int `json:"images"`
// IOTune 'json:"iotune" - it is a dictionary // IOTune 'json:"iotune" - it is a dictionary
Name string `json:"name"` Name string `json:"name"`
// Order `json:"order"` // Order `json:"order"`
ParentId int `json:"parentId"` ParentId int `json:"parentId"`
PciSlot int `json:"pciSlot"` PciSlot int `json:"pciSlot"`
// ResID string `json:"resId"` // ResID string `json:"resId"`
// ResName string `json:"resName"` // ResName string `json:"resName"`
// Params string `json:"params"` // Params string `json:"params"`
Pool string `json:"pool"` Pool string `json:"pool"`
PurgeTime uint64 `json:"purgeTime"` PurgeTime uint64 `json:"purgeTime"`
// Role string `json:"role"` // Role string `json:"role"`
SepType string `json:"sepType"` SepType string `json:"sepType"`
SepID int `json:"sepid"` SepID int `json:"sepid"`
SizeMax int `json:"sizeMax"` SizeMax int `json:"sizeMax"`
SizeUsed int `json:"sizeUsed"` // sum over all snapshots of this disk to report total consumed space SizeUsed int `json:"sizeUsed"` // sum over all snapshots of this disk to report total consumed space
Snapshots []SnapshotRecord `json:"snapshots"` Snapshots []SnapshotRecord `json:"snapshots"`
Status string `json:"status"` Status string `json:"status"`
TechStatus string `json:"techStatus"` TechStatus string `json:"techStatus"`
Type string `json:"type"` Type string `json:"type"`
ComputeID int `json:"vmid"` ComputeID int `json:"vmid"`
} }
type OsUserRecord struct { type OsUserRecord struct {
Guid string `json:"guid"` Guid string `json:"guid"`
Login string `json:"login"` Login string `json:"login"`
Password string `json:"password"` Password string `json:"password"`
PubKey string `json:"pubkey"` PubKey string `json:"pubkey"`
} }
const ComputeGetAPI = "/restmachine/cloudapi/compute/get" const ComputeGetAPI = "/restmachine/cloudapi/compute/get"
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"`
AccountName string `json:"accountName"` AccountName string `json:"accountName"`
Arch string `json:"arch"` Arch string `json:"arch"`
BootDiskSize int `json:"bootdiskSize"` BootDiskSize int `json:"bootdiskSize"`
CloneReference int `json:"cloneReference"` CloneReference int `json:"cloneReference"`
Clones []int `json:"clones"` Clones []int `json:"clones"`
Cpus int `json:"cpus"` Cpus int `json:"cpus"`
Desc string `json:"desc"` Desc string `json:"desc"`
Disks []DiskRecord `json:"disks"` Disks []DiskRecord `json:"disks"`
GridID int `json:"gid"` GridID int `json:"gid"`
ID uint `json:"id"` ID uint `json:"id"`
ImageID int `json:"imageId"` ImageID int `json:"imageId"`
ImageName string `json:"imageName"` ImageName string `json:"imageName"`
Interfaces []InterfaceRecord `json:"interfaces` Interfaces []InterfaceRecord `json:"interfaces`
LockStatus string `json:"lockStatus"` LockStatus string `json:"lockStatus"`
ManagerID int `json:"managerId"` ManagerID int `json:"managerId"`
ManagerType string `json:"manageType"` ManagerType string `json:"manageType"`
Name string `json:"name"` Name string `json:"name"`
NatableVinsID int `json:"natableVinsId"` NatableVinsID int `json:"natableVinsId"`
NatableVinsIP string `json:"natableVinsIp"` NatableVinsIP string `json:"natableVinsIp"`
NatableVinsName string `json:"natableVinsName"` NatableVinsName string `json:"natableVinsName"`
NatableVinsNet string `json:"natableVinsNetwork"` NatableVinsNet string `json:"natableVinsNetwork"`
NatableVinsNetName string `json:"natableVinsNetworkName"` NatableVinsNetName string `json:"natableVinsNetworkName"`
OsUsers []OsUserRecord `json:"osUsers"` OsUsers []OsUserRecord `json:"osUsers"`
Ram int `json:"ram"` Ram int `json:"ram"`
RgID int `json:"rgId"` RgID int `json:"rgId"`
RgName string `json:"rgName"` RgName string `json:"rgName"`
SnapSets []SnapSetRecord `json:"snapSets"` SnapSets []SnapSetRecord `json:"snapSets"`
Status string `json:"status"` Status string `json:"status"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
TechStatus string `json:"techStatus"` TechStatus string `json:"techStatus"`
TotalDiskSize int `json:"totalDiskSize"` TotalDiskSize int `json:"totalDiskSize"`
UpdatedBy string `json:"updatedBy"` UpdatedBy string `json:"updatedBy"`
UpdateTime uint64 `json:"updateTime"` UpdateTime uint64 `json:"updateTime"`
UserManaged bool `json:"userManaged"` UserManaged bool `json:"userManaged"`
Vgpus []int `json:"vgpus"` Vgpus []int `json:"vgpus"`
VinsConnected int `json:"vinsConnected"` VinsConnected int `json:"vinsConnected"`
VirtualImageID int `json:"virtualImageId"` VirtualImageID int `json:"virtualImageId"`
} }
// //
// structures related to /restmachine/cloudapi/images/list API // structures related to /restmachine/cloudapi/images/list API
// //
type ImageRecord struct { type ImageRecord struct {
AccountID uint `json:"accountId"` AccountID uint `json:"accountId"`
Arch string `json:"architecture` Arch string `json:"architecture`
BootType string `json:"bootType"` BootType string `json:"bootType"`
IsBootable boo `json:"bootable"` IsBootable bool `json:"bootable"`
IsCdrom bool `json:"cdrom"` IsCdrom bool `json:"cdrom"`
Desc string `json:"description"` Desc string `json:"description"`
IsHotResize bool `json:"hotResize"` IsHotResize bool `json:"hotResize"`
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Pool string `json:"pool"` Pool string `json:"pool"`
SepID int `json:"sepid"` SepID int `json:"sepid"`
Size int `json:"size"` Size int `json:"size"`
Status string `json:"status"` Status string `json:"status"`
Type string `json:"type"` Type string `json:"type"`
Username string `json:"username"` Username string `json:"username"`
IsVirtual bool `json:"virtual"` IsVirtual bool `json:"virtual"`
} }
const ImagesListAPI = "/restmachine/cloudapi/images/list" const ImagesListAPI = "/restmachine/cloudapi/images/list"
type ImagesListParam struct {
AccountID int `json:"accountId"`
}
type ImagesListResp []ImageRecord type ImagesListResp []ImageRecord
// //
// structures related to /cloudapi/extnet/list API // structures related to /cloudapi/extnet/list API
// //
type ExtNetRecord struct { type ExtNetRecord struct {
Name string `json:"name"` Name string `json:"name"`
ID uint `json:"id"` ID uint `json:"id"`
IPCIDR string `json:"ipcidr"` IPCIDR string `json:"ipcidr"`
} }
const ExtNetListAPI = "/restmachine/cloudapi/extnet/list" const ExtNetListAPI = "/restmachine/cloudapi/extnet/list"
type ExtNetListParam struct {
AccountID int `json:"accountId"`
}
type ExtNetListResp []ExtNetRecord
type ExtNetListResp []ExtNetRecord
// //
// structures related to /cloudapi/accounts/list API // structures related to /cloudapi/accounts/list API
// //
type AccountRecord struct { type AccountRecord struct {
ACLs []UserAclRecord `json:"acl"` // ACLs []UserAclRecord `json:"acl"`
CreatedTime uint64 `json:"creationTime"` // CreatedTime uint64 `json:"creationTime"`
DeletedTime uint64 `json:"deletionTime"` // DeletedTime uint64 `json:"deletionTime"`
ID int `json:"id"` ID int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Status string `json:"status"` Status string `json:"status"`
UpdatedTime uint64 `json:"updateTime"` // UpdatedTime uint64 `json:"updateTime"`
} }
const AccountsListAPI = "/restmachine/cloudapi/accounts/list" const AccountsGetAPI = "/restmachine/cloudapi/accounts/get" // returns AccountRecord superset
const AccountsListAPI = "/restmachine/cloudapi/accounts/list" // returns list of abdridged info about accounts
type AccountsListResp []AccountRecord type AccountsListResp []AccountRecord
// //
// structures related to /cloudapi/portforwarding/list API // structures related to /cloudapi/portforwarding/list API
// //
type PfwRecord struct { type PfwRecord struct {
ID int `json:"id"` ID int `json:"id"`
LocalIP string `json:"localIp` LocalIP string `json:"localIp`
LocalPort int `json:"localPort"` LocalPort int `json:"localPort"`
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
PublicPortEnd int `json:"publicPortEnd"` PublicPortEnd int `json:"publicPortEnd"`
PublicPortStart int `json:"publicPortStart"` PublicPortStart int `json:"publicPortStart"`
ComputeID int `json:"vmId"` ComputeID int `json:"vmId"`
} }
const ComputePfwListAPI = "/restmachine/cloudapi/compute/pfwList" const ComputePfwListAPI = "/restmachine/cloudapi/compute/pfwList"
type ComputePfwListResp []PfwRecord type ComputePfwListResp []PfwRecord
type ComputePfwAddParam struct {
ComputeID int `json:"computeId"`
PublicPortStart int `json:"publicPortStart"`
PublicPortEnd int `json:"publicPortEnd"`
LocalBasePort int `json:"localBasePort"`
Protocol string `json:"proto"`
}
const ComputePfwAddAPI = "/restmachine/cloudapi/compute/pfwAdd" const ComputePfwAddAPI = "/restmachine/cloudapi/compute/pfwAdd"
type ComputePfwDelParam struct {
ComputeID int `json:"computeId"`
RuleID int `json:"ruleId"`
PublicPortStart int `json:"publicPortStart"`
PublicPortEnd int `json:"publicPortEnd"`
LocalBasePort int `json:"localBasePort"`
Protocol string `json:"proto"`
}
const ComputePfwDelAPI = "/restmachine/cloudapi/compute/pfwDel" const ComputePfwDelAPI = "/restmachine/cloudapi/compute/pfwDel"
// //
// structures related to /cloudapi/compute/net Attach/Detach API // structures related to /cloudapi/compute/net Attach/Detach API
// //
type ComputeNetAttachParam struct {
ComputeID int `json:"computeId"`
NetType string `json:"netType"`
NetID int `json:"netId"`
IPAddr string `json:"apAddr"`
}
const ComputeNetAttachAPI = "/restmachine/cloudapi/compute/netAttach" const ComputeNetAttachAPI = "/restmachine/cloudapi/compute/netAttach"
type ComputeNetDetachParam struct {
ComputeID int `json:"computeId"`
IPAddr string `json:"apAddr"`
MAC string `json:"mac"`
}
const ComputeNetDetachAPI = "/restmachine/cloudapi/compute/netDetach" const ComputeNetDetachAPI = "/restmachine/cloudapi/compute/netDetach"
// //
// structures related to /cloudapi/compute/disk Attach/Detach API // structures related to /cloudapi/compute/disk Attach/Detach API
// //
type ComputeDiskManipulationParam struct {
ComputeID int `json:"computeId"`
DiskID int `json:"diskId"`
}
const ComputeDiskAttachAPI = "/restmachine/cloudapi/compute/diskAttach" const ComputeDiskAttachAPI = "/restmachine/cloudapi/compute/diskAttach"
const ComputeDiskDetachAPI = "/restmachine/cloudapi/compute/diskDetach" const ComputeDiskDetachAPI = "/restmachine/cloudapi/compute/diskDetach"
// //
// structures related to /cloudapi/disks/create // structures related to /cloudapi/disks/create
// //
type DiskCreateParam struct { type DiskCreateParam struct {
AccountID int `json:"accountId` AccountID int `json:"accountId`
GridID int `json:"gid"` GridID int `json:"gid"`
Name string `json:"string"` Name string `json:"string"`
Description string `json:"description"` Description string `json:"description"`
Size int `json:"size"` Size int `json:"size"`
Type string `json:"type"` Type string `json:"type"`
SepID int `json:"sep_id"` SepID int `json:"sep_id"`
Pool string `json:"pool"` Pool string `json:"pool"`
} }
const DiskCreateAPI = "/restmachine/cloudapi/disks/create" const DiskCreateAPI = "/restmachine/cloudapi/disks/create"
// //
// structures related to /cloudapi/disks/get // structures related to /cloudapi/disks/get
// //
type DisksGetParam struct {
DiskID int `json:"diskId`
}
const DisksCreateAPI = "/restmachine/cloudapi/disks/create" const DisksCreateAPI = "/restmachine/cloudapi/disks/create"
const DisksGetAPI = "/restmachine/cloudapi/disks/get" // Returns single DiskRecord on success const DisksGetAPI = "/restmachine/cloudapi/disks/get" // Returns single DiskRecord on success
const DisksListAPI = "/restmachine/cloudapi/disks/list" // Returns list of DiskRecord on success const DisksListAPI = "/restmachine/cloudapi/disks/list" // Returns list of DiskRecord on success

@ -18,34 +18,32 @@ limitations under the License.
package decort package decort
import ( import (
"strings" "strings"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation" "github.com/hashicorp/terraform/helper/validation"
// "github.com/hashicorp/terraform/terraform" // "github.com/hashicorp/terraform/terraform"
) )
var decsController *ControllerCfg var decsController *ControllerCfg
func Provider() *schema.Provider { func Provider() *schema.Provider {
return &schema.Provider { return &schema.Provider{
Schema: map[string]*schema.Schema { Schema: map[string]*schema.Schema{
"authenticator": { "authenticator": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
StateFunc: stateFuncToLower, StateFunc: stateFuncToLower,
ValidateFunc: validation.StringInSlice([]string{"oauth2", "legacy", "jwt"}, true), // ignore case while validating ValidateFunc: validation.StringInSlice([]string{"oauth2", "legacy", "jwt"}, true), // ignore case while validating
Description: "Authentication mode to use when connecting to DECS cloud API. Should be one of 'oauth2', 'legacy' or 'jwt'.", Description: "Authentication mode to use when connecting to DECORT cloud API. Should be one of 'oauth2', 'legacy' or 'jwt'.",
}, },
"oauth2_url": { "oauth2_url": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
StateFunc: stateFuncToLower, StateFunc: stateFuncToLower,
DefaultFunc: schema.EnvDefaultFunc("DECS_OAUTH2_URL", nil), DefaultFunc: schema.EnvDefaultFunc("DECORT_OAUTH2_URL", nil),
Description: "The Oauth2 application URL in 'oauth2' authentication mode.", Description: "OAuth2 application URL in 'oauth2' authentication mode.",
}, },
"controller_url": { "controller_url": {
@ -53,67 +51,70 @@ func Provider() *schema.Provider {
Required: true, Required: true,
ForceNew: true, ForceNew: true,
StateFunc: stateFuncToLower, StateFunc: stateFuncToLower,
Description: "The URL of DECS Cloud controller to use. API calls will be directed to this URL.", Description: "URL of DECORT Cloud controller to use. API calls will be directed to this URL.",
}, },
"user": { "user": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_USER", nil), DefaultFunc: schema.EnvDefaultFunc("DECORT_USER", nil),
Description: "The user name for DECS cloud API operations in 'legacy' authentication mode.", Description: "User name for DECORT cloud API operations in 'legacy' authentication mode.",
}, },
"password": { "password": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_PASSWORD", nil), DefaultFunc: schema.EnvDefaultFunc("DECORT_PASSWORD", nil),
Description: "The user password for DECS cloud API operations in 'legacy' authentication mode.", Description: "User password for DECORT cloud API operations in 'legacy' authentication mode.",
}, },
"app_id": { "app_id": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_APP_ID", nil), DefaultFunc: schema.EnvDefaultFunc("DECORT_APP_ID", nil),
Description: "Application ID to access DECS cloud API in 'oauth2' authentication mode.", Description: "Application ID to access DECORT cloud API in 'oauth2' authentication mode.",
}, },
"app_secret": { "app_secret": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_APP_SECRET", nil), DefaultFunc: schema.EnvDefaultFunc("DECSORTAPP_SECRET", nil),
Description: "Application secret to access DECS cloud API in 'oauth2' authentication mode.", Description: "Application secret to access DECORT cloud API in 'oauth2' authentication mode.",
}, },
"jwt": { "jwt": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_JWT", nil), DefaultFunc: schema.EnvDefaultFunc("DECS_JWT", nil),
Description: "JWT to access DECS cloud API in 'jwt' authentication mode.", Description: "JWT to access DECORT cloud API in 'jwt' authentication mode.",
}, },
"allow_unverified_ssl": { "allow_unverified_ssl": {
Type: schema.TypeBool, Type: schema.TypeBool,
Optional: true, Optional: true,
Default: false, Default: false,
Description: "If set, DECS API will allow unverifiable SSL certificates.", Description: "If true, DECORT API will not verify SSL certificates. Use this with caution and in trusted environments only!",
}, },
}, },
ResourcesMap: map[string]*schema.Resource { ResourcesMap: map[string]*schema.Resource{
"decort_resgroup": resourceResgroup(), "decort_resgroup": resourceResgroup(),
"decort_kvmx86": resourceKvmX86(), "decort_kvmvm": resourceCompute(),
"decort_disk": resourceDisk(), "decort_disk": resourceDisk(),
"decort_vins": resourceVins(), "decort_vins": resourceVins(),
// "decort_pfw": resourcePfw(),
}, },
DataSourcesMap: map[string]*schema.Resource { DataSourcesMap: map[string]*schema.Resource{
// "decort_account": dataSourceAccount(),
"decort_resgroup": dataSourceResgroup(), "decort_resgroup": dataSourceResgroup(),
"decs_kvmx86": dataSourceCompute(), "decort_kvmvm": dataSourceCompute(),
"decort_image": dataSourceImage(), "decort_image": dataSourceImage(),
"decort_disk": dataSourceDisk(), "decort_disk": dataSourceDisk(),
"decort_vins": dataSourceVins(), "decort_vins": dataSourceVins(),
// "decort_pfw": dataSourcePfw(),
}, },
ConfigureFunc: providerConfigure, ConfigureFunc: providerConfigure,
} }
} }
@ -128,4 +129,4 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
return nil, err return nil, err
} }
return decsController, nil return decsController, nil
} }

@ -16,42 +16,43 @@ limitations under the License.
*/ */
/* /*
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
Technology platfom. Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/ */
package decort package decort
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
// "net/url" // "net/url"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
// "github.com/hashicorp/terraform/helper/validation" // "github.com/hashicorp/terraform/helper/validation"
) )
func flattenResgroup(d *schema.ResourceData, rg_facts string) error { func flattenResgroup(d *schema.ResourceData, rg_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 resourceRsgroupExists(...) method // from resourceRsgroupExists(...) method
log.Debugf("%s", rg_facts) // log.Debugf("%s", rg_facts)
log.Debugf("flattenResgroup: ready to decode response body from %q", CloudspacesGetAPI) log.Debugf("flattenResgroup: ready to decode response body from API")
details := ResgroupGetResp{} details := ResgroupGetResp{}
err := json.Unmarshal([]byte(rg_facts), &details) err := json.Unmarshal([]byte(rg_facts), &details)
if err != nil { if err != nil {
return err return err
} }
log.Debugf("flattenResgroup: decoded ResGroup name %q / ID %d, account ID %d, public IP %q", log.Debugf("flattenResgroup: decoded RG name %q / ID %d, account ID %d",
details.Name, details.ID, details.AccountID, details.PublicIP) details.Name, details.ID, details.AccountID)
d.SetId(fmt.Sprintf("%d", details.ID)) d.SetId(fmt.Sprintf("%d", details.ID))
d.Set("rg_id", details.ID)
d.Set("name", details.Name) d.Set("name", details.Name)
d.Set("account_name", details.AccountName)
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("desc", details.Description)
@ -73,7 +74,7 @@ func dataSourceResgroupRead(d *schema.ResourceData, m interface{}) error {
rg_facts, err := utilityResgroupCheckPresence(d, m) rg_facts, err := utilityResgroupCheckPresence(d, m)
if rg_facts == "" { if rg_facts == "" {
// if empty string is returned from utilityResgroupCheckPresence then there is no // if empty string is returned from utilityResgroupCheckPresence then there is no
// such resource group and err tells so - just return it to the calling party // such resource group and err tells so - just return it to the calling party
d.SetId("") // ensure ID is empty in this case d.SetId("") // ensure ID is empty in this case
return err return err
} }
@ -81,92 +82,97 @@ func dataSourceResgroupRead(d *schema.ResourceData, m interface{}) error {
return flattenResgroup(d, rg_facts) return flattenResgroup(d, rg_facts)
} }
func dataSourceResgroup() *schema.Resource { func dataSourceResgroup() *schema.Resource {
return &schema.Resource { return &schema.Resource{
SchemaVersion: 1, SchemaVersion: 1,
Read: dataSourceResgroupRead, Read: dataSourceResgroupRead,
Timeouts: &schema.ResourceTimeout { Timeouts: &schema.ResourceTimeout{
Read: &Timeout30s, Read: &Timeout30s,
Default: &Timeout60s, Default: &Timeout60s,
}, },
Schema: map[string]*schema.Schema { Schema: map[string]*schema.Schema{
"name": { "name": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Optional: true,
Description: "Name of this resource group. Names are case sensitive and unique within the context of an account.", Description: "Name of the resource group. Names are case sensitive and unique within the context of an account.",
},
"rg_id": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: "Unique ID of the resource group. If this ID is specified, then resource group name is ignored.",
}, },
"account": &schema.Schema { "account_name": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: Optional,
Description: "Name of the account, 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, Optional: Optional,
Description: "Unique ID of the account, which this resource group belongs to.", Description: "Unique ID of the account, which this resource group belongs to. If account ID is specified, then account name is ignored.",
}, },
"desc": &schema.Schema { "desc": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "User-defined text description of this resource group.", Description: "User-defined text description of this resource group.",
}, },
"grid_id": &schema.Schema { "grid_id": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
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": { "quotas": {
Type: schema.TypeList, Type: schema.TypeList,
Optional: true, Optional: true,
MaxItems: 1, MaxItems: 1,
Elem: &schema.Resource { Elem: &schema.Resource{
Schema: quotaRgSubresourceSchema(), // this is a dictionary 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": { "status": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Current status of this resource group.", Description: "Current status of this resource group.",
}, },
"def_net": &schema.Schema { "def_net": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Type of the default network for this resource group.", Description: "Type of the default network for this resource group.",
}, },
"def_net_id": &schema.Schema { "def_net_id": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "ID of the default network for this resource group (if any).", Description: "ID of the default network for this resource group (if any).",
}, },
"vins": { "vins": {
Type: schema.TypeList, // this is a list of ints Type: schema.TypeList, // this is a list of ints
Computed: true, Computed: true,
MaxItems: LimitMaxVinsPerResgroup, MaxItems: LimitMaxVinsPerResgroup,
Elem: &schema.Schema { Elem: &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
}, },
Description: "List of VINs deployed in this resource group.", Description: "List of VINs deployed in this resource group.",
}, },
"computes": { "computes": {
Type: schema.TypeList, //t his is a list of ints Type: schema.TypeList, //t his is a list of ints
Computed: true, Computed: true,
Elem: &schema.Schema { Elem: &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
}, },
Description: "List of computes deployed in this resource group.", Description: "List of computes deployed in this resource group.",
}, },

@ -15,57 +15,51 @@ limitations under the License.
*/ */
/* /*
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
Technology platfom. Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/ */
package decort package decort
import ( import (
"fmt" "fmt"
"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 {
// First validate that we have all parameters required to create the new Resource Group // First validate that we have all parameters required to create the new Resource Group
arg_set := false
account_name, arg_set := d.GetOk("account") // Valid account ID is required to create new resource group
if !arg_set { // obtain Account ID by account name - it should not be zero on success
return fmt.Errorf("Cannot create new RG: missing account.") validated_account_id, err := utilityGetAccountIdBySchema(d, m)
if err != nil {
return err
} }
rg_name, arg_set := d.GetOk("name") rg_name, arg_set := d.GetOk("name")
if !arg_set { if !arg_set {
return fmt.Errorf("Cannot create new RG: missing name.") return fmt.Errorf("Cannot create new RG: missing name.")
} }
grid_id, arg_set := d.GetOk("grid_id") grid_id, arg_set := d.GetOk("grid_id")
if !arg_set { if !arg_set {
return fmt.Errorf("Cannot create new RG %q for account %q: missing Grid ID.", return fmt.Errorf("Cannot create new RG %q in account ID %d: missing Grid ID.",
rg_name.(string), account_name.(string)) rg_name.(string), validated_account_id)
} }
// all required parameters are set in the schema - we can continue with RG creation // 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", log.Debugf("resourceResgroupCreate: called for RG name %q, account ID %d",
account_name.(string), rg_name.(string)) rg_name.(string), validated_account_id)
// Valid account ID is required to create new resource group
// obtain Account ID by account name - it should not be zero on success
validated_account_id, err := utilityGetAccountIdByName(account_name.(string), m)
if err != nil {
return err
}
// quota settings are optional // quota settings are optional
set_quota := false set_quota := false
var quota_record QuotaRecord var quota_record QuotaRecord
arg_value, arg_set = d.GetOk("quota") arg_value, arg_set = d.GetOk("quota")
if arg_set { if arg_set {
log.Debugf("resourceResgroupCreate: setting Quota on RG requested") log.Debugf("resourceResgroupCreate: setting Quota on RG requested")
@ -75,34 +69,34 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
controller := m.(*ControllerCfg) controller := m.(*ControllerCfg)
log.Debugf("resourceResgroupCreate: called by user %q for RG name %q, account %q / ID %d, Grid ID %d", log.Debugf("resourceResgroupCreate: called by user %q for RG name %q, account %q / ID %d, Grid ID %d",
controller.getdecortUsername(), controller.getdecortUsername(),
rg_name.(string), account_name.(string), validated_account_id, gird_id.(int)) 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"`
GridId int `json:"gid"` GridId int `json:"gid"`
Name string `json:"name"` Name string `json:"name"`
Ram int `json:"maxMemoryCapacity"` Ram int `json:"maxMemoryCapacity"`
Disk int `json:"maxVDiskCapacity"` Disk int `json:"maxVDiskCapacity"`
Cpu int `json:"maxCPUCapacity"` Cpu int `json:"maxCPUCapacity"`
NetTraffic int `json:"maxNetworkPeerTransfer"` NetTraffic int `json:"maxNetworkPeerTransfer"`
ExtIPs int `json:"maxNumPublicIP"` ExtIPs int `json:"maxNumPublicIP"`
Owner string `json:"owner"` Owner string `json:"owner"`
DefNet string `json:"def_net"` DefNet string `json:"def_net"`
IPCidr string `json:"ipcidr"` IPCidr string `json:"ipcidr"`
Desc string `json:"decs"` Desc string `json:"decs"`
Reason string `json:"reason"` Reason string `json:"reason"`
ExtNetID int `json:"extNetId"` ExtNetID int `json:"extNetId"`
ExtIP string `json:"extIp"` ExtIP string `json:"extIp"`
} }
*/ */
url_values := &url.Values{} url_values := &url.Values{}
url_values.Add("accountId", fmt.Sprintf("%d", validated_account_id)) url_values.Add("accountId", fmt.Sprintf("%d", validated_account_id))
url_values.Add("name", rg_name.(string)) url_values.Add("name", rg_name.(string))
url_values.Add("gid", fmt.Sprintf("%d", grid_id.(int))) url_values.Add("gid", fmt.Sprintf("%d", grid_id.(int)))
url_values.Add("owner", controller.getdecortUsername()) url_values.Add("owner", controller.getdecortUsername())
// pass quota values as set // pass quota values as set
if set_quota { if set_quota {
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quota_record.Cpu)) url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quota_record.Cpu))
@ -133,7 +127,7 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
if arg_set { if arg_set {
ulr_values.Add("extIp", ext_ip.(string)) 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)
if err != nil { if err != nil {
return err return err
@ -147,12 +141,12 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
} }
func resourceResgroupRead(d *schema.ResourceData, m interface{}) error { func resourceResgroupRead(d *schema.ResourceData, m interface{}) error {
log.Debugf("resourceResgroupRead: called for RG name %q, account name %q", log.Debugf("resourceResgroupRead: called for RG name %q, account name %q",
d.Get("name").(string), d.Get("account").(string)) d.Get("name").(string), d.Get("account_name").(string))
rg_facts, err := utilityResgroupCheckPresence(d, m) rg_facts, err := utilityResgroupCheckPresence(d, m)
if rg_facts == "" { if rg_facts == "" {
// if empty string is returned from utilityResgroupCheckPresence then there is no // if empty string is returned from utilityResgroupCheckPresence then there is no
// such resource group and err tells so - just return it to the calling party // such resource group and err tells so - just return it to the calling party
d.SetId("") // ensure ID is empty d.SetId("") // ensure ID is empty
return err return err
} }
@ -161,8 +155,8 @@ func resourceResgroupRead(d *schema.ResourceData, m interface{}) error {
} }
func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error { func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
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))
do_update := false do_update := false
@ -179,7 +173,7 @@ func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
url_values.Add("name", name_new.(string)) url_values.Add("name", name_new.(string))
} }
} }
quota_value, quota_set := d.GetOk("quota") quota_value, quota_set := d.GetOk("quota")
if quota_set { if quota_set {
log.Debugf("resourceResgroupUpdate: quota specified - looking for deltas from the old quota.") log.Debugf("resourceResgroupUpdate: quota specified - looking for deltas from the old quota.")
@ -192,25 +186,25 @@ func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
log.Debugf("resourceResgroupUpdate: Cpu diff %d <- %d", quotarecord_new.Cpu, quotarecord_old.Cpu) log.Debugf("resourceResgroupUpdate: Cpu diff %d <- %d", quotarecord_new.Cpu, quotarecord_old.Cpu)
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quotarecord_new.Cpu)) url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quotarecord_new.Cpu))
} }
if quotarecord_new.Disk != quotarecord_old.Disk { if quotarecord_new.Disk != quotarecord_old.Disk {
do_update = true do_update = true
log.Debugf("resourceResgroupUpdate: Disk diff %d <- %d", quotarecord_new.Disk, quotarecord_old.Disk) log.Debugf("resourceResgroupUpdate: Disk diff %d <- %d", quotarecord_new.Disk, quotarecord_old.Disk)
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotarecord_new.Disk)) url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotarecord_new.Disk))
} }
if quotarecord_new.Ram != quotarecord_old.Ram { if quotarecord_new.Ram != quotarecord_old.Ram {
do_update = true do_update = true
log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotarecord_new.Ram, quotarecord_old.Ram) log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotarecord_new.Ram, quotarecord_old.Ram)
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotarecord_new.Ram)) url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotarecord_new.Ram))
} }
if quotarecord_new.ExtTraffic != quotarecord_old.ExtTraffic { if quotarecord_new.ExtTraffic != quotarecord_old.ExtTraffic {
do_update = true do_update = true
log.Debugf("resourceResgroupUpdate: NetTraffic diff %d <- %d", quotarecord_new.ExtTraffic, quotarecord_old.ExtTraffic) log.Debugf("resourceResgroupUpdate: NetTraffic diff %d <- %d", quotarecord_new.ExtTraffic, quotarecord_old.ExtTraffic)
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quotarecord_new.NetTraffic)) url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quotarecord_new.NetTraffic))
} }
if quotarecord_new.ExtIPs != quotarecord_old.ExtIPs { if quotarecord_new.ExtIPs != quotarecord_old.ExtIPs {
do_update = true do_update = true
log.Debugf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotarecord_new.ExtIPs, quotarecord_old.ExtIPs) log.Debugf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotarecord_new.ExtIPs, quotarecord_old.ExtIPs)
@ -237,19 +231,19 @@ func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
} else { } else {
log.Debugf("resourceResgroupUpdate: no difference between old and new state - no update on the RG 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)
} }
func resourceResgroupDelete(d *schema.ResourceData, m interface{}) error { func resourceResgroupDelete(d *schema.ResourceData, m interface{}) error {
// NOTE: this method forcibly destroys target resource group with flag "permanently", so there is no way to // NOTE: this method forcibly destroys target resource group with flag "permanently", so there is no way to
// restore the destroyed resource group as well all Computes & VINSes that existed in it // restore the destroyed resource group as well all Computes & VINSes that existed in it
log.Debugf("resourceResgroupDelete: called for RG name %q, account name %q", log.Debugf("resourceResgroupDelete: called for RG name %q, account name %q",
d.Get("name").(string), d.Get("account").(string)) d.Get("name").(string), d.Get("account_name").(string))
rg_facts, err := utilityResgroupCheckPresence(d, m) rg_facts, err := utilityResgroupCheckPresence(d, m)
if rg_facts == "" { if rg_facts == "" {
// the target RG does not exist - in this case according to Terraform best practice // the target RG does not exist - in this case according to Terraform best practice
// we exit from Destroy method without error // we exit from Destroy method without error
return nil return nil
} }
@ -282,7 +276,7 @@ func resourceResgroupExists(d *schema.ResourceData, m interface{}) (bool, error)
} }
func resourceResgroup() *schema.Resource { func resourceResgroup() *schema.Resource {
return &schema.Resource { return &schema.Resource{
SchemaVersion: 1, SchemaVersion: 1,
Create: resourceResgroupCreate, Create: resourceResgroupCreate,
@ -291,7 +285,7 @@ func resourceResgroup() *schema.Resource {
Delete: resourceResgroupDelete, Delete: resourceResgroupDelete,
Exists: resourceResgroupExists, Exists: resourceResgroupExists,
Timeouts: &schema.ResourceTimeout { Timeouts: &schema.ResourceTimeout{
Create: &Timeout180s, Create: &Timeout180s,
Read: &Timeout30s, Read: &Timeout30s,
Update: &Timeout180s, Update: &Timeout180s,
@ -299,103 +293,109 @@ func resourceResgroup() *schema.Resource {
Default: &Timeout60s, Default: &Timeout60s,
}, },
Schema: map[string]*schema.Schema { Schema: map[string]*schema.Schema{
"name": &schema.Schema { "name": &schema.Schema{
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 account.", Description: "Name of this resource group. Names are case sensitive and unique within the context of a account.",
}, },
"account": &schema.Schema { "account_id": &schema.Schema{
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{
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Optional: true,
Description: "Name of the account, which this resource group belongs to.", Description: "Name of the account, which this resource group belongs to.",
}, },
"def_net": &schema.Schema { "def_net": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Default: "PRIVATE" Default: "PRIVATE",
Description: "Type of the network, which this resource group will use as default for its computes - PRIVATE or PUBLIC or NONE.", Description: "Type of the network, which this resource group will use as default for its computes - PRIVATE or PUBLIC or NONE.",
}, },
"ipcidr": &schema.Schema { "ipcidr": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Description: "Address of the netowrk inside the private network segment (aka ViNS) if def_net=PRIVATE", Description: "Address of the netowrk inside the private network segment (aka ViNS) if def_net=PRIVATE",
}, },
"ext_net_id": &schema.Schema { "ext_net_id": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
Default: 0, Default: 0,
Description: "ID of the external network, which this resource group will use as default for its computes if def_net=PUBLIC", 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 { "ext_ip": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Description: "IP address on the external netowrk to request, if def_net=PUBLIC", Description: "IP address on the external netowrk to request, if def_net=PUBLIC",
}, },
"account_id": &schema.Schema { "account_id": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "Unique ID of the account, which this resource group belongs to.", Description: "Unique ID of the account, which this resource group belongs to.",
}, },
"grid_id": &schema.Schema { "grid_id": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Required: true, Required: true,
Description: "Unique ID of the grid, where this resource group is deployed.", Description: "Unique ID of the grid, where this resource group is deployed.",
}, },
"quota": { "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: "Quota settings for this resource group.", Description: "Quota settings for this resource group.",
}, },
"desc": { "desc": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Description: "User-defined text description of this resource group.", Description: "User-defined text description of this resource group.",
}, },
"status": { "status": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Current status of this resource group.", Description: "Current status of this resource group.",
}, },
"def_net_id": &schema.Schema { "def_net_id": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "ID of the default network for this resource group (if any).", Description: "ID of the default network for this resource group (if any).",
}, },
"vins": { "vins": {
Type: schema.TypeList, // this is a list of ints Type: schema.TypeList, // this is a list of ints
Computed: true, Computed: true,
MaxItems: LimitMaxVinsPerResgroup, MaxItems: LimitMaxVinsPerResgroup,
Elem: &schema.Schema { Elem: &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
}, },
Description: "List of VINs deployed in this resource group.", Description: "List of VINs deployed in this resource group.",
}, },
"computes": { "computes": {
Type: schema.TypeList, // this is a list of ints Type: schema.TypeList, // this is a list of ints
Computed: true, Computed: true,
Elem: &schema.Schema { Elem: &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
}, },
Description: "List of computes deployed in this resource group.", Description: "List of computes deployed in this resource group.",
}, },
}, },
} }
} }

@ -16,20 +16,20 @@ limitations under the License.
*/ */
/* /*
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
Technology platfom. Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/ */
package decort package decort
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
"net/url" "net/url"
// "strconv" // "strconv"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -52,18 +52,18 @@ func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupGetResp,
} }
/* /*
ret := &ResgroupConfig{} ret := &ResgroupConfig{}
ret.AccountID = model.AccountID ret.AccountID = model.AccountID
ret.Location = model.Location ret.Location = model.Location
ret.Name = model.Name ret.Name = model.Name
ret.ID = rgid ret.ID = rgid
ret.GridID = model.GridID ret.GridID = model.GridID
ret.ExtIP = model.ExtIP // legacy field for VDC - this will eventually become obsoleted by true Resource Groups ret.ExtIP = model.ExtIP // legacy field for VDC - this will eventually become obsoleted by true Resource Groups
// Quota ResgroupQuotaConfig // Quota ResgroupQuotaConfig
// Network NetworkConfig // Network NetworkConfig
*/ */
log.Debugf("utilityResgroupConfigGet: account ID %d, GridID %d, Name %s", log.Debugf("utilityResgroupConfigGet: account ID %d, GridID %d, Name %s",
model.AccountID, model.GridID, model.Name) model.AccountID, model.GridID, model.Name)
return model, nil return model, nil
} }
@ -71,31 +71,58 @@ func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupGetResp,
// On success this function returns a string, as returned by API rg/get, which could be unmarshalled // On success this function returns a string, as returned by API rg/get, which could be unmarshalled
// into ResgroupGetResp structure // 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 one of the following algorithms depending
// If succeeded, it returns non empty string that contains JSON formatted facts about the // on the parameters passed:
// - if resource group ID is specified -> by RG ID
// - if resource group name is specifeid -> by RG name and either account ID or account name
//
// If succeeded, it returns non empty string that contains JSON formatted facts about the
// resource group as returned by cloudspaces/get API call. // resource group as returned by cloudspaces/get API call.
// Otherwise it returns empty string and meaningful error. // Otherwise it returns empty string and meaningful error.
// //
// NOTE: As our provider always deletes RGs permanently, there is no "restore" method and // NOTE: As our provider always deletes RGs permanently, there is no "restore" method and
// consequently we are not interested in matching RGs in DELETED state. Hence, we call // consequently we are not interested in matching RGs in DELETED state. Hence, we call
// .../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 Terraform resource Exists method. // method for the Terraform resource Exists method.
// //
name := d.Get("name").(string)
account_name := d.Get("account").(string)
controller := m.(*ControllerCfg) controller := m.(*ControllerCfg)
url_values := &url.Values{} url_values := &url.Values{}
rg_id, arg_set := d.GetOk("rg_id")
if arg_set {
// go straight for the RG by its ID
log.Debugf("utilityResgroupCheckPresence: locating RG by its ID %d", rg_id.(int))
url_values.Add("rgId", fmt.Sprintf("%d", rg_id.(int)))
rg_facts, err := controller.decortAPICall("POST", ResgroupGetAPI, url_values)
if err != nil {
return "", err
}
return rg_facts, nil
}
rg_name, arg_set := d.GetOk("name")
if !arg_set {
// no RG ID and no RG name - we cannot locate resource group in this case
return "", fmt.Error("Cannot check resource group presence if name is empty and no resource group ID specified.")
}
// Valid account ID is required to locate a resource group
// obtain Account ID by account name - it should not be zero on success
validated_account_id, err := utilityGetAccountIdBySchema(d, m)
if err != nil {
return err
}
url_values.Add("includedeleted", "false") url_values.Add("includedeleted", "false")
body_string, err := controller.decortAPICall("POST", ResgroupListAPI, url_values) body_string, err := controller.decortAPICall("POST", ResgroupListAPI, url_values)
if err != nil { if err != nil {
return "", err return "", err
} }
// 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 := ResgroupListResp{} model := ResgroupListResp{}
err = json.Unmarshal([]byte(body_string), &model) err = json.Unmarshal([]byte(body_string), &model)
if err != nil { if err != nil {
@ -104,15 +131,15 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
log.Debugf("utilityResgroupCheckPresence: traversing decoded Json of length %d", len(model)) log.Debugf("utilityResgroupCheckPresence: traversing decoded Json of length %d", len(model))
for index, item := range model { for index, item := range model {
// need to match RG by name & account name // match by RG name & account ID
if item.Name == name && item.AccountName == account_name { if item.Name == rg_name.(string) && item.AccountID == validated_account_id {
log.Debugf("utilityResgroupCheckPresence: match RG name %q / ID %d, account %q at index %d", log.Debugf("utilityResgroupCheckPresence: match RG name %q / ID %d, account ID %d at index %d",
item.Name, item.ID, item.AccountName, index) item.Name, item.ID, item.AccountID, index)
// not all required information is returned by rg/list API, so we need to initiate one more // not all required information is returned by rg/list API, so we need to initiate one more
// call to rg/get to obtain extra data to complete Resource population. // call to rg/get to obtain extra data to complete Resource population.
// Namely, we need to extract resource quota settings // Namely, we need resource quota settings
req_values := &url.Values{} req_values := &url.Values{}
req_values.Add("rgId", fmt.Sprintf("%d", item.ID)) req_values.Add("rgId", fmt.Sprintf("%d", item.ID))
body_string, err := controller.decortAPICall("POST", ResgroupGetAPI, req_values) body_string, err := controller.decortAPICall("POST", ResgroupGetAPI, req_values)
if err != nil { if err != nil {
@ -123,32 +150,5 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
} }
} }
return "", fmt.Errorf("Cannot find RG name %q owned by account %q", name, account_name) return "", fmt.Errorf("Cannot find RG name %q owned by account ID %d", name, validated_account_id)
} }
func utilityGetAccountIdByName(account_name string, m interface{}) (int, error) {
controller := m.(*ControllerCfg)
url_values := &url.Values{}
body_string, err := controller.decortAPICall("POST", AccountsListAPI, url_values)
if err != nil {
return 0, err
}
model := AccountsListResp{}
err = json.Unmarshal([]byte(body_string), &model)
if err != nil {
return 0, err
}
log.Debugf("utilityGetAccountIdByName: traversing decoded Json of length %d", len(model))
for index, item := range model {
// need to match Account by name
if item.Name == account_name {
log.Debugf("utilityGetAccountIdByName: match Account name %q / ID %d at index %d",
item.Name, item.ID, index)
return item.ID, nil
}
}
return 0, fmt.Errorf("Cannot find account %q for the current user. Check account name and your access rights", account_name)
}

@ -1,5 +1,5 @@
/* /*
Copyright (c) 2019 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");
@ -15,10 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package decs package decort
import ( import (
"fmt" "fmt"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -26,7 +25,7 @@ import (
) )
func makeSshKeysConfig(arg_list []interface{}) (sshkeys []SshKeyConfig, count int) { func makeSshKeysConfig(arg_list []interface{}) (sshkeys []SshKeyConfig, count int) {
count = len(arg_list) count = len(arg_list)
if count < 1 { if count < 1 {
return nil, 0 return nil, 0
} }
@ -48,13 +47,13 @@ func makeSshKeysArgString(sshkeys []SshKeyConfig) string {
// It is designed to be passed as "userdata" argument of virtual machine create API call. // It is designed to be passed as "userdata" argument of virtual machine create API call.
// The following format is expected: // The following format is expected:
// '{"users": [{"ssh-authorized-keys": ["SSH_PUBCIC_KEY_VALUE"], "shell": "SHELL_VALUE", "name": "USERNAME_VALUE"}, {...}, ]}' // '{"users": [{"ssh-authorized-keys": ["SSH_PUBCIC_KEY_VALUE"], "shell": "SHELL_VALUE", "name": "USERNAME_VALUE"}, {...}, ]}'
/* /*
`%s\n `%s\n
- name: %s\n - name: %s\n
ssh-authorized-keys: ssh-authorized-keys:
- %s\n - %s\n
shell: /bin/bash` shell: /bin/bash`
*/ */
if len(sshkeys) < 1 { if len(sshkeys) < 1 {
return "" return ""
@ -70,18 +69,18 @@ func makeSshKeysArgString(sshkeys []SshKeyConfig) string {
return out return out
} }
func sshSubresourceSchema() map[string]*schema.Schema { func sshSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema { rets := map[string]*schema.Schema{
"user": { "user": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
Description: "Name of the user on the guest OS of the new VM, for which the following SSH key will be authorized.", Description: "Name of the guest OS user of a new compute, for which the following SSH key will be authorized.",
}, },
"public_key": { "public_key": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
Description: "Public part of SSH key to authorize to the specified user on the VM being created.", Description: "Public SSH key to authorize to the specified guest OS user on the compute being created.",
}, },
"shell": { "shell": {
@ -94,4 +93,3 @@ func sshSubresourceSchema() map[string]*schema.Schema {
return rets return rets
} }

Loading…
Cancel
Save