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 {
// keys in this map should correspond to the Schema definition
// 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
}
@ -71,8 +83,22 @@ func parseComputeInterfaces(ifaces []InterfaceRecord) []interface{} {
for i, value := range ifaces {
// Keys in this map should correspond to the Schema definition
// 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
}
@ -93,7 +119,7 @@ func flattenCompute(d *schema.ResourceData, comp_facts string) error {
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.Set("compute_id", model.ID)
@ -119,25 +145,15 @@ func flattenCompute(d *schema.ResourceData, comp_facts string) error {
}
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 {
return err
}
}
if len(model.GuestLogins) > 0 {
log.Printf("flattenCompute: calling parseGuestLogins")
guest_logins := parseGuestLogins(model.GuestLogins)
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 {
log.Debugf("flattenCompute: calling parseGuestLogins for %d logins", len(model.GuestLogins))
if err = d.Set("guest_logins", parseGuestLogins(model.GuestLogins)); err != nil {
return err
}
}
@ -263,7 +279,7 @@ func dataSourceCompute() *schema.Resource {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource {
Schema: interfaceSubresourceSchema(),
Schema: interfaceSubresourceSchemaMake(),
},
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.")
}
account_id, acc_id_set := d.GetOk("account_id")
if !acc_id_set {
account_name, arg_set := d.GetOkd("account_name")
if !arg_set {
return "", fmt.Error("Cannot locate disk by name %s if neither account ID nor account name are set", disk_name.(string))
}
// Valid account ID is required to locate disks
// 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("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)
if err != nil {
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))
for _, item := range disks_list {
// 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)
// 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>
Licensed under the Apache License, Version 2.0 (the "License");
@ -34,39 +34,41 @@ import (
"github.com/hashicorp/terraform/helper/validation"
)
func dataSourceImageRead(d *schema.ResourceData, m interface{}) error {
name := d.Get("name").(string)
rgid, rgid_set := d.GetOk("rgid")
tenant_id, tenant_set := d.GetOk("tenant_id")
// rg_id, rgid_set := d.GetOk("rg_id")
account_id, account_set := d.GetOk("account_id")
controller := m.(*ControllerCfg)
url_values := &url.Values{}
if tenant_set {
url_values.Add("accountId", fmt.Sprintf("%d",tenant_id.(int)))
}
if rgid_set {
url_values.Add("cloudspaceId", fmt.Sprintf("%d",rgid.(int)))
if account_set {
url_values.Add("accountId", fmt.Sprintf("%d", account_id.(int)))
}
body_string, err := controller.decortAPICall("POST", ImagesListAPI, url_values)
if err != nil {
return err
}
log.Printf("dataSourceImageRead: ready to decode response body")
log.Debugf("dataSourceImageRead: ready to decode response body from %q", ImagesListAPI)
model := ImagesListResp{}
err = json.Unmarshal([]byte(body_string), &model)
if err != nil {
return err
}
log.Printf("%#v", model)
log.Printf("dataSourceImageRead: traversing decoded JSON of length %d", len(model))
// log.Printf("%#v", model)
log.Debugf("dataSourceImageRead: traversing decoded JSON of length %d", len(model))
for index, item := range model {
// need to match VM by name
// need to match Image by name
if item.Name == 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)
return nil
}
@ -93,18 +95,49 @@ func dataSourceImage() *schema.Resource {
Description: "Name of the OS image to locate. This parameter is case sensitive.",
},
"tenant_id": {
"account_id": {
Type: schema.TypeInt,
Optional: true,
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.",
},
"arch": {
Type: schema.TypeString,
Computed: true,
Description: "Binary architecture this image is created for.",
},
"rgid": {
"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,
Optional: true,
ValidateFunc: validation.IntAtLeast(1),
Description: "ID of the resource group to limit image search to.",
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.
*/
package decs
package decort
import (
"log"
"github.com/hashicorp/terraform/helper/schema"
// "github.com/hashicorp/terraform/helper/validation"
)
func flattenGuestLogins(logins []GuestLoginRecord) []interface{} {
func parseGuestLogins(logins []OsUserRecord) []interface{} {
var result = make([]interface{}, len(logins))
elem := make(map[string]interface{})
@ -34,28 +33,28 @@ func flattenGuestLogins(logins []GuestLoginRecord) []interface{} {
elem["guid"] = value.Guid
elem["login"] = value.Login
elem["password"] = value.Password
elem["public_key"] = value.PubKey
result[index] = elem
log.Printf("flattenGuestLogins: parsed element %d - login %q",
index, value.Login)
log.Debugf("parseGuestLogins: parsed element %d - login %q", index, value.Login)
}
return result
}
func loginsSubresourceSchema() map[string]*schema.Schema {
func loginsSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "GUID of this guest user.",
Description: "GUID of this guest OS user.",
},
"login": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "Login name of this guest user.",
Description: "Login name of this guest OS user.",
},
"password": {
@ -63,7 +62,14 @@ func loginsSubresourceSchema() map[string]*schema.Schema {
Optional: true,
Default: "",
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.",
},
}

@ -22,11 +22,9 @@ Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/
package decort
import (
"time"
)
@ -79,6 +77,7 @@ type ResgroupRecord struct {
}
const ResgroupListAPI = "/restmachine/cloudapi/rg/list"
type ResgroupListResp []ResgroupRecord
//
@ -90,6 +89,7 @@ const ResgroupCreateAPI= "/restmachine/cloudapi/rg/create"
// structures related to /cloudapi/rg/update API call
//
const ResgroupUpdateAPI = "/restmachine/cloudapi/rg/update"
type ResgroupUpdateParam struct {
RgId int `json:"rgId"`
Name string `json:"name"`
@ -128,6 +128,7 @@ type UsageRecord struct {
}
const ResgroupGetAPI = "/restmachine/cloudapi/rg/get"
type ResgroupGetResp struct {
ACLs []UserAclRecord `json:"ACLs"`
Usage UsageRecord `json:"Resources"`
@ -158,6 +159,7 @@ type ResgroupGetResp struct {
// structures related to /cloudapi/rg/update API
//
const ResgroupUpdateAPI = "/restmachine/cloudapi/rg/update"
type ResgroupUpdateParam struct {
ID uint `json:"rgId"`
Name string `json:"name"`
@ -190,7 +192,9 @@ type ComputeBriefRecord struct { // this is a brief compute specifiaction as ret
Status string `json:"status"`
TechStatus string `json:"techStatus"`
}
const RgListComputesAPI = "/restmachine/cloudapi/rg/listComputes"
type RgListComputesResp []ComputeBriefRecord
//
@ -198,6 +202,7 @@ type RgListComputesResp []ComputeBriefRecord
//
const KvmX86CreateAPI = "/restmachine/cloudapi/kvmx86/create"
const KvmPPCCreateAPI = "/restmachine/cloudapi/kvmppc/create"
type KvmVmCreateParam struct { // this is unified structure for both x86 and PPC based KVM VMs creation
RgID uint `json:"rgId"`
Name string `json:"name"`
@ -220,21 +225,29 @@ const ComputeDeleteAPI = "/restmachine/cloudapi/compute/delete"
// 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 {
ConnID int `json:"connId"`
ConnType string `json:"connType"`
ConnID int `json:"connId"` // This is VLAN ID or VxLAN ID, depending on ConnType
ConnType string `json:"connType"` // Either "VLAN" or "VXLAN" tag
DefaultGW string `json:"defGw"`
Guid string `json:"guid"`
IPAddress string `json:"ipAddress"` // without trailing network mask, i.e. "192.168.1.3"
MAC string `json:"mac"`
Name string `json:"name"`
NetID int `json:"netId"`
NetMaks int `json:"netMask"`
NetType string `json:"netType"`
NetID int `json:"netId"` // This is either ExtNet ID or ViNS ID, depending on NetType
NetMask int `json:"netMask"`
NetType string `json:"netType"` // Either "EXTNET" or "VINS" tag
PciSlot int `json:"pciSlot"`
Target string `json:"target"`
Type string `json:"type"`
VNFs []int `json:"vnfs"`
QOS InterfaceQosRecord `json:"qos"`
}
type SnapSetRecord struct {
@ -283,6 +296,7 @@ type ComputeRecord struct {
}
const ComputeListAPI = "/restmachine/cloudapi/compute/list"
type ComputeListResp []ComputeRecord
//
@ -341,6 +355,7 @@ type OsUserRecord struct {
}
const ComputeGetAPI = "/restmachine/cloudapi/compute/get"
type ComputeGetResp struct {
// ACLs `json:"ACL"` - it is a dictionary, special parsing required
AccountID int `json:"accountId"`
@ -390,7 +405,7 @@ type ImageRecord struct {
AccountID uint `json:"accountId"`
Arch string `json:"architecture`
BootType string `json:"bootType"`
IsBootable boo `json:"bootable"`
IsBootable bool `json:"bootable"`
IsCdrom bool `json:"cdrom"`
Desc string `json:"description"`
IsHotResize bool `json:"hotResize"`
@ -406,9 +421,7 @@ type ImageRecord struct {
}
const ImagesListAPI = "/restmachine/cloudapi/images/list"
type ImagesListParam struct {
AccountID int `json:"accountId"`
}
type ImagesListResp []ImageRecord
//
@ -421,26 +434,25 @@ type ExtNetRecord struct {
}
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
//
type AccountRecord struct {
ACLs []UserAclRecord `json:"acl"`
CreatedTime uint64 `json:"creationTime"`
DeletedTime uint64 `json:"deletionTime"`
// ACLs []UserAclRecord `json:"acl"`
// CreatedTime uint64 `json:"creationTime"`
// DeletedTime uint64 `json:"deletionTime"`
ID int `json:"id"`
Name string `json:"name"`
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
//
@ -457,53 +469,23 @@ type PfwRecord struct {
}
const ComputePfwListAPI = "/restmachine/cloudapi/compute/pfwList"
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"
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"
//
// 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"
type ComputeNetDetachParam struct {
ComputeID int `json:"computeId"`
IPAddr string `json:"apAddr"`
MAC string `json:"mac"`
}
const ComputeNetDetachAPI = "/restmachine/cloudapi/compute/netDetach"
//
// 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 ComputeDiskDetachAPI = "/restmachine/cloudapi/compute/diskDetach"
@ -521,14 +503,12 @@ type DiskCreateParam struct {
SepID int `json:"sep_id"`
Pool string `json:"pool"`
}
const DiskCreateAPI = "/restmachine/cloudapi/disks/create"
//
// structures related to /cloudapi/disks/get
//
type DisksGetParam struct {
DiskID int `json:"diskId`
}
const DisksCreateAPI = "/restmachine/cloudapi/disks/create"
const DisksGetAPI = "/restmachine/cloudapi/disks/get" // Returns single DiskRecord on success

@ -18,13 +18,11 @@ limitations under the License.
package decort
import (
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
// "github.com/hashicorp/terraform/terraform"
)
var decsController *ControllerCfg
@ -37,15 +35,15 @@ func Provider() *schema.Provider {
Required: true,
StateFunc: stateFuncToLower,
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": {
Type: schema.TypeString,
Optional: true,
StateFunc: stateFuncToLower,
DefaultFunc: schema.EnvDefaultFunc("DECS_OAUTH2_URL", nil),
Description: "The Oauth2 application URL in 'oauth2' authentication mode.",
DefaultFunc: schema.EnvDefaultFunc("DECORT_OAUTH2_URL", nil),
Description: "OAuth2 application URL in 'oauth2' authentication mode.",
},
"controller_url": {
@ -53,65 +51,68 @@ func Provider() *schema.Provider {
Required: true,
ForceNew: true,
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": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_USER", nil),
Description: "The user name for DECS cloud API operations in 'legacy' authentication mode.",
DefaultFunc: schema.EnvDefaultFunc("DECORT_USER", nil),
Description: "User name for DECORT cloud API operations in 'legacy' authentication mode.",
},
"password": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_PASSWORD", nil),
Description: "The user password for DECS cloud API operations in 'legacy' authentication mode.",
DefaultFunc: schema.EnvDefaultFunc("DECORT_PASSWORD", nil),
Description: "User password for DECORT cloud API operations in 'legacy' authentication mode.",
},
"app_id": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_APP_ID", nil),
Description: "Application ID to access DECS cloud API in 'oauth2' authentication mode.",
DefaultFunc: schema.EnvDefaultFunc("DECORT_APP_ID", nil),
Description: "Application ID to access DECORT cloud API in 'oauth2' authentication mode.",
},
"app_secret": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("DECS_APP_SECRET", nil),
Description: "Application secret to access DECS cloud API in 'oauth2' authentication mode.",
DefaultFunc: schema.EnvDefaultFunc("DECSORTAPP_SECRET", nil),
Description: "Application secret to access DECORT cloud API in 'oauth2' authentication mode.",
},
"jwt": {
Type: schema.TypeString,
Optional: true,
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": {
Type: schema.TypeBool,
Optional: true,
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{
"decort_resgroup": resourceResgroup(),
"decort_kvmx86": resourceKvmX86(),
"decort_kvmvm": resourceCompute(),
"decort_disk": resourceDisk(),
"decort_vins": resourceVins(),
// "decort_pfw": resourcePfw(),
},
DataSourcesMap: map[string]*schema.Resource{
// "decort_account": dataSourceAccount(),
"decort_resgroup": dataSourceResgroup(),
"decs_kvmx86": dataSourceCompute(),
"decort_kvmvm": dataSourceCompute(),
"decort_image": dataSourceImage(),
"decort_disk": dataSourceDisk(),
"decort_vins": dataSourceVins(),
// "decort_pfw": dataSourcePfw(),
},
ConfigureFunc: providerConfigure,

@ -25,33 +25,34 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p
package decort
import (
"encoding/json"
"fmt"
"log"
// "net/url"
"github.com/hashicorp/terraform/helper/schema"
// "github.com/hashicorp/terraform/helper/validation"
)
func flattenResgroup(d *schema.ResourceData, rg_facts string) error {
// NOTE: this function modifies ResourceData argument - as such it should never be called
// from resourceRsgroupExists(...) method
log.Debugf("%s", rg_facts)
log.Debugf("flattenResgroup: ready to decode response body from %q", CloudspacesGetAPI)
// log.Debugf("%s", rg_facts)
log.Debugf("flattenResgroup: ready to decode response body from API")
details := ResgroupGetResp{}
err := json.Unmarshal([]byte(rg_facts), &details)
if err != nil {
return err
}
log.Debugf("flattenResgroup: decoded ResGroup name %q / ID %d, account ID %d, public IP %q",
details.Name, details.ID, details.AccountID, details.PublicIP)
log.Debugf("flattenResgroup: decoded RG name %q / ID %d, account ID %d",
details.Name, details.ID, details.AccountID)
d.SetId(fmt.Sprintf("%d", details.ID))
d.Set("rg_id", details.ID)
d.Set("name", details.Name)
d.Set("account_name", details.AccountName)
d.Set("account_id", details.AccountID)
d.Set("grid_id", details.GridID)
d.Set("desc", details.Description)
@ -81,7 +82,6 @@ func dataSourceResgroupRead(d *schema.ResourceData, m interface{}) error {
return flattenResgroup(d, rg_facts)
}
func dataSourceResgroup() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
@ -96,20 +96,26 @@ func dataSourceResgroup() *schema.Resource {
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of this resource group. Names are case sensitive and unique within the context of an account.",
Optional: true,
Description: "Name of the resource group. Names are case sensitive and unique within the context of an account.",
},
"account": &schema.Schema {
"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_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Required: Optional,
Description: "Name of the account, which this resource group belongs to.",
},
"account_id": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
Description: "Unique ID of the account, which this resource group belongs to.",
Optional: Optional,
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{

@ -24,44 +24,38 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p
package decort
import (
"fmt"
"log"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
// First validate that we have all parameters required to create the new Resource Group
arg_set := false
account_name, arg_set := d.GetOk("account")
if !arg_set {
return fmt.Errorf("Cannot create new RG: missing account.")
// 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 := utilityGetAccountIdBySchema(d, m)
if err != nil {
return err
}
rg_name, arg_set := d.GetOk("name")
if !arg_set {
return fmt.Errorf("Cannot create new RG: missing name.")
}
grid_id, arg_set := d.GetOk("grid_id")
if !arg_set {
return fmt.Errorf("Cannot create new RG %q for account %q: missing Grid ID.",
rg_name.(string), account_name.(string))
return fmt.Errorf("Cannot create new RG %q in account ID %d: missing Grid ID.",
rg_name.(string), validated_account_id)
}
// all required parameters are set in the schema - we can continue with RG creation
log.Debugf("resourceResgroupCreate: called for RG name %q, account name %q",
account_name.(string), rg_name.(string))
// Valid account ID is required to create new resource group
// obtain Account ID by account name - it should not be zero on success
validated_account_id, err := utilityGetAccountIdByName(account_name.(string), m)
if err != nil {
return err
}
log.Debugf("resourceResgroupCreate: called for RG name %q, account ID %d",
rg_name.(string), validated_account_id)
// quota settings are optional
set_quota := false
@ -148,7 +142,7 @@ func resourceResgroupCreate(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",
d.Get("name").(string), d.Get("account").(string))
d.Get("name").(string), d.Get("account_name").(string))
rg_facts, err := utilityResgroupCheckPresence(d, m)
if rg_facts == "" {
// if empty string is returned from utilityResgroupCheckPresence then there is no
@ -245,7 +239,7 @@ 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
// 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",
d.Get("name").(string), d.Get("account").(string))
d.Get("name").(string), d.Get("account_name").(string))
rg_facts, err := utilityResgroupCheckPresence(d, m)
if rg_facts == "" {
@ -306,16 +300,22 @@ func resourceResgroup() *schema.Resource {
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,
Required: true,
Optional: true,
Description: "Name of the account, which this resource group belongs to.",
},
"def_net": &schema.Schema{
Type: schema.TypeString,
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.",
},

@ -25,11 +25,11 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p
package decort
import (
"encoding/json"
"fmt"
"log"
"net/url"
// "strconv"
"github.com/hashicorp/terraform/helper/schema"
@ -71,7 +71,11 @@ func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupGetResp,
// On success this function returns a string, as returned by API rg/get, which could be unmarshalled
// into ResgroupGetResp structure
func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
// 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
// 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.
// Otherwise it returns empty string and meaningful error.
@ -83,19 +87,42 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
// This function does not modify its ResourceData argument, so it is safe to use it as core
// method for the Terraform resource Exists method.
//
name := d.Get("name").(string)
account_name := d.Get("account").(string)
controller := m.(*ControllerCfg)
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")
body_string, err := controller.decortAPICall("POST", ResgroupListAPI, url_values)
if err != nil {
return "", err
}
log.Debugf("%s", body_string)
log.Debugf("utilityResgroupCheckPresence: ready to decode response body from %q", ResgroupListAPI)
// log.Debugf("%s", body_string)
// log.Debugf("utilityResgroupCheckPresence: ready to decode response body from %q", ResgroupListAPI)
model := ResgroupListResp{}
err = json.Unmarshal([]byte(body_string), &model)
if err != nil {
@ -104,14 +131,14 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
log.Debugf("utilityResgroupCheckPresence: traversing decoded Json of length %d", len(model))
for index, item := range model {
// need to match RG by name & account name
if item.Name == name && item.AccountName == account_name {
log.Debugf("utilityResgroupCheckPresence: match RG name %q / ID %d, account %q at index %d",
item.Name, item.ID, item.AccountName, index)
// match by RG name & account ID
if item.Name == rg_name.(string) && item.AccountID == validated_account_id {
log.Debugf("utilityResgroupCheckPresence: match RG name %q / ID %d, account ID %d at index %d",
item.Name, item.ID, item.AccountID, index)
// 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.
// Namely, we need to extract resource quota settings
// Namely, we need resource quota settings
req_values := &url.Values{}
req_values.Add("rgId", fmt.Sprintf("%d", item.ID))
body_string, err := controller.decortAPICall("POST", ResgroupGetAPI, req_values)
@ -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)
}
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)
return "", fmt.Errorf("Cannot find RG name %q owned by account ID %d", name, validated_account_id)
}

@ -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>
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.
*/
package decs
package decort
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
@ -70,18 +69,18 @@ func makeSshKeysArgString(sshkeys []SshKeyConfig) string {
return out
}
func sshSubresourceSchema() map[string]*schema.Schema {
func sshSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"user": {
Type: schema.TypeString,
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": {
Type: schema.TypeString,
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": {
@ -94,4 +93,3 @@ func sshSubresourceSchema() map[string]*schema.Schema {
return rets
}

Loading…
Cancel
Save