Add account handler and refine other code. No testing yet!
This commit is contained in:
136
decort/account_data_source.go
Normal file
136
decort/account_data_source.go
Normal file
@@ -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."
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
153
decort/account_utility.go
Normal file
153
decort/account_utility.go
Normal file
@@ -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");
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -93,18 +95,49 @@ func dataSourceImage() *schema.Resource {
|
|||||||
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.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
Description: "Binary architecture this image is created for.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"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,
|
Type: schema.TypeInt,
|
||||||
Optional: true,
|
Computed: true,
|
||||||
ValidateFunc: validation.IntAtLeast(1),
|
Description: "Size of the image in GB.",
|
||||||
Description: "ID of the resource group to limit image search to.",
|
},
|
||||||
|
|
||||||
|
"status": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
Description: "Current model status of this disk.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
330
decort/interface_subresource.go
Normal file
330
decort/interface_subresource.go
Normal file
@@ -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.",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,9 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -79,6 +77,7 @@ type ResgroupRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ResgroupListAPI = "/restmachine/cloudapi/rg/list"
|
const ResgroupListAPI = "/restmachine/cloudapi/rg/list"
|
||||||
|
|
||||||
type ResgroupListResp []ResgroupRecord
|
type ResgroupListResp []ResgroupRecord
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -90,6 +89,7 @@ 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"`
|
||||||
@@ -128,6 +128,7 @@ type UsageRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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"`
|
||||||
@@ -158,6 +159,7 @@ type ResgroupGetResp struct {
|
|||||||
// 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"`
|
||||||
@@ -190,7 +192,9 @@ type ComputeBriefRecord struct { // this is a brief compute specifiaction as ret
|
|||||||
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,6 +202,7 @@ 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"`
|
||||||
@@ -220,21 +225,29 @@ 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 {
|
||||||
@@ -283,6 +296,7 @@ type ComputeRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ComputeListAPI = "/restmachine/cloudapi/compute/list"
|
const ComputeListAPI = "/restmachine/cloudapi/compute/list"
|
||||||
|
|
||||||
type ComputeListResp []ComputeRecord
|
type ComputeListResp []ComputeRecord
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -341,6 +355,7 @@ type OsUserRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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"`
|
||||||
@@ -390,7 +405,7 @@ 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"`
|
||||||
@@ -406,9 +421,7 @@ type ImageRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -421,26 +434,25 @@ type ExtNetRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -457,53 +469,23 @@ type PfwRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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"
|
||||||
@@ -521,14 +503,12 @@ type DiskCreateParam struct {
|
|||||||
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
|
||||||
|
|||||||
@@ -18,13 +18,11 @@ 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
|
||||||
@@ -37,15 +35,15 @@ func Provider() *schema.Provider {
|
|||||||
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,65 +51,68 @@ 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,
|
||||||
|
|||||||
@@ -25,33 +25,34 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p
|
|||||||
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)
|
||||||
@@ -81,7 +82,6 @@ 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,
|
||||||
@@ -96,20 +96,26 @@ func dataSourceResgroup() *schema.Resource {
|
|||||||
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.",
|
||||||
},
|
},
|
||||||
|
|
||||||
"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,
|
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{
|
||||||
|
|||||||
@@ -24,44 +24,38 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p
|
|||||||
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
|
||||||
@@ -148,7 +142,7 @@ 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
|
||||||
@@ -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
|
// 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 == "" {
|
||||||
@@ -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.",
|
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.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p
|
|||||||
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"
|
||||||
@@ -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
|
// 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
|
||||||
|
// 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
|
// 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.
|
||||||
@@ -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
|
// 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,14 +131,14 @@ 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)
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user