Merge branch '3.0/harmonization' into 'dev'

Add files

See merge request rudecs/terraform-provider-decort!32
gos_tech_4.4.3
Administrator 3 years ago
commit 1245dd17f2

@ -1,11 +1,19 @@
# terraform-provider-decort
Terraform provider для платформы Digital Energy Cloud Orchestration Technology (DECORT)
Внимание: провайдер версии rc-1.25 разработан для DECORT API 3.7.x.
Внимание: провайдер версии 3.x разработан для DECORT API 3.8.x.
Для более старых версий можно использовать:
- DECORT API 3.7.x - версия провайдера rc-1.25
- DECORT API 3.6.x - версия провайдера rc-1.10
- DECORT API до 3.6.0 - terraform DECS provider (https://github.com/rudecs/terraform-provider-decs)
## Режимы работы
Провайдер позволяет работать в двух режимах:
- Режим пользователя,
- Режим администратора.
Для переключения между режимами используйте флаг DECORT_ADMIN_MODE.
Вики проекта: https://github.com/rudecs/terraform-provider-decort/wiki
## Возможности провайдера
- Работа с Compute instances,
- Работа с disks,

@ -1,10 +1,19 @@
# terraform-provider-decort
Terraform provider for Digital Energy Cloud Orchestration Technology (DECORT) platform
NOTE: provider rc-1.25 is designed for DECORT API 3.7.x. For older API versions please use:
NOTE: provider 3.x is designed for DECORT API 3.8.x. For older API versions please use:
- DECORT API 3.7.x versions - provider verion rc-1.25
- DECORT API 3.6.x versions - provider version rc-1.10
- DECORT API versions prior to 3.6.0 - Terraform DECS provider (https://github.com/rudecs/terraform-provider-decs)
## Working modes
The provider support two working modes:
- User mode,
- Administator mode.
Use flag DECORT_ADMIN_MODE for swithcing beetwen modes.
See user guide at https://github.com/rudecs/terraform-provider-decort/wiki
## Features
- Work with Compute instances,
- Work with disks,

@ -362,6 +362,9 @@ func (config *ControllerCfg) DecortAPICall(ctx context.Context, method string, a
if err != nil {
return "", err
}
req = req.WithContext(ctx)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Content-Length", strconv.Itoa(len(params_str)))
req.Header.Set("Accept", "application/json")

@ -25,6 +25,7 @@ import (
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/bservice"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/disks"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/extnet"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/image"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/kvmvm"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/locations"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/rg"
@ -41,6 +42,7 @@ func NewDataSourcesMap() map[string]*schema.Resource {
"decort_vins": vins.DataSourceVins(),
"decort_snapshot_list": snapshot.DataSourceSnapshotList(),
"decort_vgpu": vgpu.DataSourceVGPU(),
"decort_disk": disks.DataSourceDisk(),
"decort_disk_list": disks.DataSourceDiskList(),
"decort_rg_list": rg.DataSourceRgList(),
"decort_account_list": account.DataSourceAccountList(),
@ -67,6 +69,8 @@ func NewDataSourcesMap() map[string]*schema.Resource {
"decort_vins_list": vins.DataSourceVinsList(),
"decort_locations_list": locations.DataSourceLocationsList(),
"decort_location_url": locations.DataSourceLocationUrl(),
"decort_image_list": image.DataSourceImageList(),
"decort_image": image.DataSourceImage(),
// "decort_pfw": dataSourcePfw(),
}

@ -24,6 +24,7 @@ import (
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/account"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/bservice"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/disks"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/image"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/k8s"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/kvmvm"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/pfw"
@ -45,5 +46,7 @@ func NewRersourcesMap() map[string]*schema.Resource {
"decort_account": account.ResourceAccount(),
"decort_bservice": bservice.ResourceBasicService(),
"decort_bservice_group": bservice.ResourceBasicServiceGroup(),
"decort_image": image.ResourceImage(),
"decort_image_virtual": image.ResourceImageVirtual(),
}
}

@ -21,7 +21,8 @@ package cloudbroker
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudapi/disks"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/account"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/disks"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/grid"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/image"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/pcidevice"
@ -30,8 +31,18 @@ import (
func NewDataSourcesMap() map[string]*schema.Resource {
return map[string]*schema.Resource{
"decort_image": image.DataSourceImage(),
"decort_account": account.DataSourceAccount(),
"decort_account_list": account.DataSourceAccountList(),
"decort_account_computes_list": account.DataSourceAccountComputesList(),
"decort_account_deleted_list": account.DataSourceAccountDeletedList(),
"decort_account_disks_list": account.DataSourceAccountDisksList(),
"decort_account_flipgroups_list": account.DataSourceAccountFlipGroupsList(),
"decort_account_rg_list": account.DataSourceAccountRGList(),
"decort_account_vins_list": account.DataSourceAccountVinsList(),
"decort_account_audits_list": account.DataSourceAccountAuditsList(),
"decort_disk": disks.DataSourceDisk(),
"decort_disk_list": disks.DataSourceDiskList(),
"decort_image": image.DataSourceImage(),
"decort_grid": grid.DataSourceGrid(),
"decort_grid_list": grid.DataSourceGridList(),
"decort_image_list": image.DataSourceImageList(),

@ -21,13 +21,23 @@ package cloudbroker
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/account"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/disks"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/image"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/k8s"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/kvmvm"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/pcidevice"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/pfw"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/rg"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/sep"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/snapshot"
"github.com/rudecs/terraform-provider-decort/internal/service/cloudbroker/vins"
)
func NewRersourcesMap() map[string]*schema.Resource {
return map[string]*schema.Resource{
"decort_account": account.ResourceAccount(),
"decort_disk": disks.ResourceDisk(),
"decort_image": image.ResourceImage(),
"decort_virtual_image": image.ResourceVirtualImage(),
"decort_cdrom_image": image.ResourceCDROMImage(),
@ -35,5 +45,12 @@ func NewRersourcesMap() map[string]*schema.Resource {
"decort_pcidevice": pcidevice.ResourcePcidevice(),
"decort_sep": sep.ResourceSep(),
"decort_sep_config": sep.ResourceSepConfig(),
"decort_resgroup": rg.ResourceResgroup(),
"decort_kvmvm": kvmvm.ResourceCompute(),
"decort_vins": vins.ResourceVins(),
"decort_pfw": pfw.ResourcePfw(),
"decort_k8s": k8s.ResourceK8s(),
"decort_k8s_wg": k8s.ResourceK8sWg(),
"decort_snapshot": snapshot.ResourceSnapshot(),
}
}

@ -22,6 +22,7 @@ package provider
import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"golang.org/x/net/context"
@ -105,24 +106,22 @@ func Provider() *schema.Provider {
DataSourcesMap: selectSchema(true),
ConfigureFunc: providerConfigure,
ConfigureContextFunc: providerConfigure,
}
}
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
decsController, err := controller.ControllerConfigure(d)
if err != nil {
return nil, err
return nil, diag.FromErr(err)
}
ctx := context.Background()
gridId, err := location.UtilityLocationGetDefaultGridID(ctx, decsController)
if err != nil {
return nil, err
return nil, diag.FromErr(err)
}
if gridId == 0 {
return nil, fmt.Errorf("providerConfigure: invalid default Grid ID = 0")
return nil, diag.FromErr(fmt.Errorf("providerConfigure: invalid default Grid ID = 0"))
}
return decsController, nil

@ -73,51 +73,6 @@ func flattenRgAcl(rgAcls []AccountAclRecord) []map[string]interface{} {
return res
}
/*uncomment for cloudbroker
func flattenAccountList(al AccountList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, acc := range al {
temp := map[string]interface{}{
"dc_location": acc.DCLocation,
"ckey": acc.CKey,
"meta": flattenMeta(acc.Meta),
"acl": flattenRgAcl(acc.Acl),
"company": acc.Company,
"companyurl": acc.CompanyUrl,
"created_by": acc.CreatedBy,
"created_time": acc.CreatedTime,
"deactivation_time": acc.DeactiovationTime,
"deleted_by": acc.DeletedBy,
"deleted_time": acc.DeletedTime,
"displayname": acc.DisplayName,
"guid": acc.GUID,
"account_id": acc.ID,
"account_name": acc.Name,
"resource_limits": flattenRgResourceLimits(acc.ResourceLimits),
"send_access_emails": acc.SendAccessEmails,
"service_account": acc.ServiceAccount,
"status": acc.Status,
"updated_time": acc.UpdatedTime,
"version": acc.Version,
"vins": acc.Vins,
}
res = append(res, temp)
}
return res
}
*/
func dataSourceAccountListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountList, err := utilityAccountListCheckPresence(ctx, d, m)
if err != nil {
@ -148,22 +103,6 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema {
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
/*uncomment for cloudbroker
"dc_location": {
Type: schema.TypeString,
Computed: true,
},
"ckey": {
Type: schema.TypeString,
Computed: true,
},
"meta": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},*/
"acl": {
Type: schema.TypeList,
Computed: true,
@ -196,48 +135,14 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema {
},
},
},
/*uncomment for cloudbroker
"company": {
Type: schema.TypeString,
Computed: true,
},
"companyurl": {
Type: schema.TypeString,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
*/
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
/*uncomment for cloudbroker
"deactivation_time": {
Type: schema.TypeFloat,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
*/
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
/*uncomment for cloudbroker
"displayname": {
Type: schema.TypeString,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
*/
"account_id": {
Type: schema.TypeInt,
Computed: true,
@ -246,49 +151,6 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema {
Type: schema.TypeString,
Computed: true,
},
/*uncomment for cloudbroker
"resource_limits": {
Type: schema.TypeList,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cu_c": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_d": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_i": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_m": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_np": {
Type: schema.TypeFloat,
Computed: true,
},
"gpu_units": {
Type: schema.TypeFloat,
Computed: true,
},
},
},
},
"send_access_emails": {
Type: schema.TypeBool,
Computed: true,
},
"service_account": {
Type: schema.TypeBool,
Computed: true,
},
*/
"status": {
Type: schema.TypeString,
Computed: true,
@ -297,19 +159,6 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema {
Type: schema.TypeInt,
Computed: true,
},
/*uncomment for cloudbroker
"version": {
Type: schema.TypeInt,
Computed: true,
},
"vins": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
*/
},
},
},

@ -37,7 +37,6 @@ import (
"strconv"
"strings"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
@ -49,22 +48,6 @@ import (
func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceAccountCreate")
if accountId, ok := d.GetOk("account_id"); ok {
if exists, err := resourceAccountExists(ctx, d, m); exists {
if err != nil {
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(accountId.(int)))
diagnostics := resourceAccountRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
return nil
}
return diag.Errorf("provided account id does not exist")
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
@ -138,7 +121,6 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, m interf
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(accountId)
d.Set("account_id", accountId)
@ -147,7 +129,42 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, m interf
return diagnostics
}
d.SetId(id.String())
urlValues = &url.Values{}
if enable, ok := d.GetOk("enable"); ok {
api := accountDisableAPI
enable := enable.(bool)
if enable {
api = accountEnableAPI
}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
_, err := c.DecortAPICall(ctx, "POST", api, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
if users, ok := d.GetOk("users"); ok {
addedUsers := users.([]interface{})
if len(addedUsers) > 0 {
for _, user := range addedUsers {
userConv := user.(map[string]interface{})
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
urlValues.Add("userId", userConv["user_id"].(string))
urlValues.Add("accesstype", strings.ToUpper(userConv["access_type"].(string)))
_, err := c.DecortAPICall(ctx, "POST", accountAddUserAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
}
}
return nil
}

@ -36,7 +36,6 @@ import (
"net/url"
"strconv"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
@ -47,24 +46,6 @@ import (
func resourceBasicServiceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceBasicServiceCreate")
if serviceId, ok := d.GetOk("service_id"); ok {
if exists, err := resourceBasicServiceExists(ctx, d, m); exists {
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(strconv.Itoa(serviceId.(int)))
d.Set("service_id", strconv.Itoa(serviceId.(int)))
diagnostics := resourceBasicServiceRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
d.SetId(id.String())
return nil
}
return diag.Errorf("provided service id does not exist")
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
@ -83,7 +64,6 @@ func resourceBasicServiceCreate(ctx context.Context, d *schema.ResourceData, m i
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(serviceId)
d.Set("service_id", serviceId)
@ -92,8 +72,6 @@ func resourceBasicServiceCreate(ctx context.Context, d *schema.ResourceData, m i
return diagnostics
}
d.SetId(id.String())
return nil
}

@ -37,7 +37,6 @@ import (
"strconv"
"strings"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@ -49,26 +48,6 @@ import (
func resourceBasicServiceGroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceBasicServiceGroupCreate")
if compgroupId, ok := d.GetOk("compgroup_id"); ok {
if _, ok := d.GetOk("service_id"); ok {
if exists, err := resourceBasicServiceGroupExists(ctx, d, m); exists {
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(strconv.Itoa(compgroupId.(int)))
d.Set("compgroup_id", strconv.Itoa(compgroupId.(int)))
diagnostics := resourceBasicServiceGroupRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
d.SetId(id.String())
return nil
}
return diag.Errorf("provided compgroup id does not exist")
}
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
@ -124,7 +103,6 @@ func resourceBasicServiceGroupCreate(ctx context.Context, d *schema.ResourceData
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(compgroupId)
d.Set("compgroup_id", compgroupId)
@ -133,8 +111,6 @@ func resourceBasicServiceGroupCreate(ctx context.Context, d *schema.ResourceData
return diagnostics
}
d.SetId(id.String())
return nil
}

@ -31,9 +31,11 @@ Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
package disks
const DisksCreateAPI = "/restmachine/cloudapi/disks/create"
const DisksGetAPI = "/restmachine/cloudapi/disks/get" // Returns single DiskRecord on success
const DisksListAPI = "/restmachine/cloudapi/disks/list" // Returns list of DiskRecord on success
const DisksResizeAPI = "/restmachine/cloudapi/disks/resize2"
const DisksRenameAPI = "/restmachine/cloudapi/disks/rename"
const DisksDeleteAPI = "/restmachine/cloudapi/disks/delete"
const disksCreateAPI = "/restmachine/cloudapi/disks/create"
const disksGetAPI = "/restmachine/cloudapi/disks/get"
const disksListAPI = "/restmachine/cloudapi/disks/list"
const disksResizeAPI = "/restmachine/cloudapi/disks/resize2"
const disksRenameAPI = "/restmachine/cloudapi/disks/rename"
const disksDeleteAPI = "/restmachine/cloudapi/disks/delete"
const disksIOLimitAPI = "/restmachine/cloudapi/disks/limitIO"
const disksRestoreAPI = "/restmachine/cloudapi/disks/restore"

@ -34,162 +34,340 @@ package disks
import (
"context"
"encoding/json"
"fmt"
// "net/url"
"github.com/google/uuid"
"github.com/rudecs/terraform-provider-decort/internal/constants"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func flattenDisk(d *schema.ResourceData, disk_facts string) error {
// This function expects disk_facts string to contain a response from disks/get API
//
// NOTE: this function modifies ResourceData argument - as such it should never be called
// from resourceDiskExists(...) method. Use utilityDiskCheckPresence instead.
log.Debugf("flattenDisk: ready to unmarshal string %s", disk_facts)
model := DiskRecord{}
err := json.Unmarshal([]byte(disk_facts), &model)
func dataSourceDiskRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
disk, err := utilityDiskCheckPresence(ctx, d, m)
if err != nil {
return err
return diag.FromErr(err)
}
log.Debugf("flattenDisk: disk ID %d, disk AccountID %d", model.ID, model.AccountID)
d.SetId(fmt.Sprintf("%d", model.ID))
// d.Set("disk_id", model.ID) - we should NOT update disk_id in the schema. If it was set - it is already set, if it wasn't - we shouldn't
d.Set("name", model.Name)
d.Set("account_id", model.AccountID)
d.Set("account_name", model.AccountName)
d.Set("size", model.SizeMax)
// d.Set("sizeUsed", model.SizeUsed)
d.Set("type", model.Type)
d.Set("image_id", model.ImageID)
d.Set("sep_id", model.SepID)
d.Set("sep_type", model.SepType)
d.Set("pool", model.Pool)
// d.Set("compute_id", model.ComputeID)
d.Set("description", model.Desc)
id := uuid.New()
d.SetId(id.String())
diskAcl, _ := json.Marshal(disk.Acl)
d.Set("account_id", disk.AccountID)
d.Set("account_name", disk.AccountName)
d.Set("acl", string(diskAcl))
d.Set("boot_partition", disk.BootPartition)
d.Set("compute_id", disk.ComputeID)
d.Set("compute_name", disk.ComputeName)
d.Set("created_time", disk.CreatedTime)
d.Set("deleted_time", disk.DeletedTime)
d.Set("desc", disk.Desc)
d.Set("destruction_time", disk.DestructionTime)
d.Set("devicename", disk.DeviceName)
d.Set("disk_path", disk.DiskPath)
d.Set("gid", disk.GridID)
d.Set("guid", disk.GUID)
d.Set("disk_id", disk.ID)
d.Set("image_id", disk.ImageID)
d.Set("images", disk.Images)
d.Set("iotune", flattenIOTune(disk.IOTune))
d.Set("iqn", disk.IQN)
d.Set("login", disk.Login)
d.Set("milestones", disk.Milestones)
d.Set("disk_name", disk.Name)
d.Set("order", disk.Order)
d.Set("params", disk.Params)
d.Set("parent_id", disk.ParentId)
d.Set("passwd", disk.Passwd)
d.Set("pci_slot", disk.PciSlot)
d.Set("pool", disk.Pool)
d.Set("purge_attempts", disk.PurgeAttempts)
d.Set("purge_time", disk.PurgeTime)
d.Set("reality_device_number", disk.RealityDeviceNumber)
d.Set("reference_id", disk.ReferenceId)
d.Set("res_id", disk.ResID)
d.Set("res_name", disk.ResName)
d.Set("role", disk.Role)
d.Set("sep_id", disk.SepID)
d.Set("sep_type", disk.SepType)
d.Set("size_max", disk.SizeMax)
d.Set("size_used", disk.SizeUsed)
d.Set("snapshots", flattendDiskSnapshotList(disk.Snapshots))
d.Set("status", disk.Status)
d.Set("tech_status", disk.TechStatus)
d.Set("type", disk.Type)
d.Set("vmid", disk.VMID)
return nil
}
func dataSourceDiskRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
disk_facts, err := utilityDiskCheckPresence(ctx, d, m)
if disk_facts == "" {
// if empty string is returned from utilityDiskCheckPresence then there is no
// such Disk and err tells so - just return it to the calling party
d.SetId("") // ensure ID is empty
return diag.FromErr(err)
}
return diag.FromErr(flattenDisk(d, disk_facts))
}
func dataSourceDiskSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of this disk. NOTE: disk names are NOT unique within an account. If disk ID is specified, disk name is ignored.",
},
"disk_id": {
Type: schema.TypeInt,
Optional: true,
Description: "ID of the disk to get. If disk ID is specified, then disk name and account ID are ignored.",
Required: true,
},
"account_id": {
Type: schema.TypeInt,
Optional: true,
Description: "ID of the account this disk belongs to. If disk ID is specified, then account ID is ignored.",
Computed: true,
},
// The rest of the data source Disk schema are all computed
"sep_id": {
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"acl": {
Type: schema.TypeString,
Computed: true,
},
"boot_partition": {
Type: schema.TypeInt,
Computed: true,
Description: "Storage end-point provider serving this disk.",
},
"pool": {
"compute_id": {
Type: schema.TypeInt,
Computed: true,
},
"compute_name": {
Type: schema.TypeString,
Computed: true,
Description: "Pool where this disk is located.",
},
"size": {
"created_time": {
Type: schema.TypeInt,
Computed: true,
Description: "Size of the disk in GB.",
},
"type": {
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Computed: true,
Description: "Type of this disk. E.g. D for data disks, B for boot.",
},
"description": {
"destruction_time": {
Type: schema.TypeInt,
Computed: true,
},
"devicename": {
Type: schema.TypeString,
Computed: true,
Description: "User-defined text description of this disk.",
},
"account_name": {
"disk_path": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the account this disk belongs to. If account ID is specified, account name is ignored.",
},
"gid": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"image_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the image, which this disk was cloned from (valid for disk clones only).",
},
"images": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"iotune": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"read_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"read_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"read_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"read_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"size_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"total_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"write_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"write_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"write_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"write_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"iqn": {
Type: schema.TypeString,
Computed: true,
},
"login": {
Type: schema.TypeString,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"disk_name": {
Type: schema.TypeString,
Computed: true,
},
"order": {
Type: schema.TypeInt,
Computed: true,
},
"params": {
Type: schema.TypeString,
Computed: true,
},
"parent_id": {
Type: schema.TypeInt,
Computed: true,
},
"passwd": {
Type: schema.TypeString,
Computed: true,
},
"pci_slot": {
Type: schema.TypeInt,
Computed: true,
},
"pool": {
Type: schema.TypeString,
Computed: true,
},
"purge_attempts": {
Type: schema.TypeInt,
Computed: true,
},
"purge_time": {
Type: schema.TypeInt,
Computed: true,
},
"reality_device_number": {
Type: schema.TypeInt,
Computed: true,
},
"reference_id": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"res_name": {
Type: schema.TypeString,
Computed: true,
},
"role": {
Type: schema.TypeString,
Computed: true,
},
"sep_id": {
Type: schema.TypeInt,
Computed: true,
},
"sep_type": {
Type: schema.TypeString,
Computed: true,
Description: "Type of the storage end-point provider serving this disk.",
},
/*
"size_max": {
Type: schema.TypeInt,
Computed: true,
},
"size_used": {
Type: schema.TypeInt,
Computed: true,
},
"snapshots": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource {
Schema: snapshotSubresourceSchemaMake(),
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Computed: true,
},
"label": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_guid": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_time": {
Type: schema.TypeInt,
Computed: true,
},
"timestamp": {
Type: schema.TypeInt,
Computed: true,
},
},
},
Description: "List of user-created snapshots for this disk."
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current model status of this disk.",
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
Description: "Current technical status of this disk.",
},
"compute_id": {
"type": {
Type: schema.TypeString,
Computed: true,
},
"vmid": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the compute instance where this disk is attached to, or 0 for unattached disk.",
},
*/
}
return rets

@ -41,11 +41,32 @@ import (
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenDiskList(dl DisksListResp) []map[string]interface{} {
func flattenIOTune(iot IOTune) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"read_bytes_sec": iot.ReadBytesSec,
"read_bytes_sec_max": iot.ReadBytesSecMax,
"read_iops_sec": iot.ReadIopsSec,
"read_iops_sec_max": iot.ReadIopsSecMax,
"size_iops_sec": iot.SizeIopsSec,
"total_bytes_sec": iot.TotalBytesSec,
"total_bytes_sec_max": iot.TotalBytesSecMax,
"total_iops_sec": iot.TotalIopsSec,
"total_iops_sec_max": iot.TotalIopsSecMax,
"write_bytes_sec": iot.WriteBytesSec,
"write_bytes_sec_max": iot.WriteBytesSecMax,
"write_iops_sec": iot.WriteIopsSec,
"write_iops_sec_max": iot.WriteIopsSecMax,
}
res = append(res, temp)
return res
}
func flattenDiskList(dl DisksList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, disk := range dl {
diskAcl, _ := json.Marshal(disk.Acl)
diskIotune, _ := json.Marshal(disk.IOTune)
temp := map[string]interface{}{
"account_id": disk.AccountID,
"account_name": disk.AccountName,
@ -64,13 +85,13 @@ func flattenDiskList(dl DisksListResp) []map[string]interface{} {
"disk_id": disk.ID,
"image_id": disk.ImageID,
"images": disk.Images,
"iotune": string(diskIotune),
"iotune": flattenIOTune(disk.IOTune),
"iqn": disk.IQN,
"login": disk.Login,
"machine_id": disk.MachineId,
"machine_name": disk.MachineName,
"milestones": disk.Milestones,
"name": disk.Name,
"disk_name": disk.Name,
"order": disk.Order,
"params": disk.Params,
"parent_id": disk.ParentId,
@ -93,7 +114,6 @@ func flattenDiskList(dl DisksListResp) []map[string]interface{} {
"tech_status": disk.TechStatus,
"type": disk.Type,
"vmid": disk.VMID,
"update_by": disk.UpdateBy,
}
res = append(res, temp)
}
@ -101,7 +121,7 @@ func flattenDiskList(dl DisksListResp) []map[string]interface{} {
}
func flattendDiskSnapshotList(sl SnapshotRecordList) []interface{} {
func flattendDiskSnapshotList(sl SnapshotList) []interface{} {
res := make([]interface{}, 0)
for _, snapshot := range sl {
temp := map[string]interface{}{
@ -231,9 +251,65 @@ func dataSourceDiskListSchemaMake() map[string]*schema.Schema {
},
},
"iotune": {
Type: schema.TypeString,
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"read_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"read_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"read_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"read_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"size_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"total_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"write_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"write_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"write_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"write_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"iqn": {
Type: schema.TypeString,
Computed: true,
@ -254,7 +330,7 @@ func dataSourceDiskListSchemaMake() map[string]*schema.Schema {
Type: schema.TypeInt,
Computed: true,
},
"name": {
"disk_name": {
Type: schema.TypeString,
Computed: true,
},
@ -374,10 +450,6 @@ func dataSourceDiskListSchemaMake() map[string]*schema.Schema {
Type: schema.TypeInt,
Computed: true,
},
"update_by": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},

@ -31,7 +31,7 @@ Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
package disks
type DiskRecord struct {
type Disk struct {
Acl map[string]interface{} `json:"acl"`
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
@ -49,7 +49,7 @@ type DiskRecord struct {
ID uint `json:"id"`
ImageID int `json:"imageId"`
Images []int `json:"images"`
IOTune map[string]interface{} `json:"iotune"`
IOTune IOTune `json:"iotune"`
IQN string `json:"iqn"`
Login string `json:"login"`
Name string `json:"name"`
@ -73,7 +73,7 @@ type DiskRecord struct {
SepID int `json:"sepId"` // NOTE: absent from compute/get output
SizeMax int `json:"sizeMax"`
SizeUsed int `json:"sizeUsed"` // sum over all snapshots of this disk to report total consumed space
Snapshots []SnapshotRecord `json:"snapshots"`
Snapshots []Snapshot `json:"snapshots"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
Type string `json:"type"`
@ -81,7 +81,7 @@ type DiskRecord struct {
VMID int `json:"vmid"`
}
type SnapshotRecord struct {
type Snapshot struct {
Guid string `json:"guid"`
Label string `json:"label"`
ResId string `json:"resId"`
@ -90,6 +90,22 @@ type SnapshotRecord struct {
TimeStamp uint64 `json:"timestamp"`
}
type SnapshotRecordList []SnapshotRecord
type SnapshotList []Snapshot
type DisksListResp []DiskRecord
type DisksList []Disk
type IOTune struct {
ReadBytesSec int `json:"read_bytes_sec"`
ReadBytesSecMax int `json:"read_bytes_sec_max"`
ReadIopsSec int `json:"read_iops_sec"`
ReadIopsSecMax int `json:"read_iops_sec_max"`
SizeIopsSec int `json:"size_iops_sec"`
TotalBytesSec int `json:"total_bytes_sec"`
TotalBytesSecMax int `json:"total_bytes_sec_max"`
TotalIopsSec int `json:"total_iops_sec"`
TotalIopsSecMax int `json:"total_iops_sec_max"`
WriteBytesSec int `json:"write_bytes_sec"`
WriteBytesSecMax int `json:"write_bytes_sec_max"`
WriteIopsSec int `json:"write_iops_sec"`
WriteIopsSecMax int `json:"write_iops_sec_max"`
}

@ -32,15 +32,15 @@ Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
package disks
import (
// "encoding/json"
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"strings"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
"github.com/rudecs/terraform-provider-decort/internal/location"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@ -49,16 +49,18 @@ import (
)
func resourceDiskCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceDiskCreate: called for Disk name %q, Account ID %d", d.Get("name").(string), d.Get("account_id").(int))
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
// accountId, gid, name, description, size, type, sep_id, pool
urlValues.Add("accountId", fmt.Sprintf("%d", d.Get("account_id").(int)))
urlValues.Add("gid", fmt.Sprintf("%d", location.DefaultGridID)) // we use default Grid ID, which was obtained along with DECORT Controller init
urlValues.Add("name", d.Get("name").(string))
urlValues.Add("size", fmt.Sprintf("%d", d.Get("size").(int)))
urlValues.Add("type", "D") // NOTE: only disks of Data type are managed via plugin
urlValues.Add("gid", fmt.Sprintf("%d", d.Get("gid").(int)))
urlValues.Add("name", d.Get("disk_name").(string))
urlValues.Add("size", fmt.Sprintf("%d", d.Get("size_max").(int)))
if typeRaw, ok := d.GetOk("type"); ok {
urlValues.Add("type", strings.ToUpper(typeRaw.(string)))
} else {
urlValues.Add("type", "D")
}
if sepId, ok := d.GetOk("sep_id"); ok {
urlValues.Add("sep_id", strconv.Itoa(sepId.(int)))
@ -68,126 +70,210 @@ func resourceDiskCreate(ctx context.Context, d *schema.ResourceData, m interface
urlValues.Add("pool", poolName.(string))
}
argVal, argSet := d.GetOk("description")
argVal, argSet := d.GetOk("desc")
if argSet {
urlValues.Add("description", argVal.(string))
}
apiResp, err := c.DecortAPICall(ctx, "POST", DisksCreateAPI, urlValues)
diskId, err := c.DecortAPICall(ctx, "POST", disksCreateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId(apiResp) // update ID of the resource to tell Terraform that the disk resource exists
diskId, _ := strconv.Atoi(apiResp)
urlValues = &url.Values{}
d.SetId(diskId) // update ID of the resource to tell Terraform that the disk resource exists
if iotuneRaw, ok := d.GetOk("iotune"); ok {
iot := iotuneRaw.([]interface{})[0]
iotune := iot.(map[string]interface{})
urlValues.Add("diskId", diskId)
urlValues.Add("iops", strconv.Itoa(iotune["total_iops_sec"].(int)))
urlValues.Add("read_bytes_sec", strconv.Itoa(iotune["read_bytes_sec"].(int)))
urlValues.Add("read_bytes_sec_max", strconv.Itoa(iotune["read_bytes_sec_max"].(int)))
urlValues.Add("read_iops_sec", strconv.Itoa(iotune["read_iops_sec"].(int)))
urlValues.Add("read_iops_sec_max", strconv.Itoa(iotune["read_iops_sec_max"].(int)))
urlValues.Add("size_iops_sec", strconv.Itoa(iotune["size_iops_sec"].(int)))
urlValues.Add("total_bytes_sec", strconv.Itoa(iotune["total_bytes_sec"].(int)))
urlValues.Add("total_bytes_sec_max", strconv.Itoa(iotune["total_bytes_sec_max"].(int)))
urlValues.Add("total_iops_sec_max", strconv.Itoa(iotune["total_iops_sec_max"].(int)))
urlValues.Add("write_bytes_sec", strconv.Itoa(iotune["write_bytes_sec"].(int)))
urlValues.Add("write_bytes_sec_max", strconv.Itoa(iotune["write_bytes_sec_max"].(int)))
urlValues.Add("write_iops_sec", strconv.Itoa(iotune["write_iops_sec"].(int)))
urlValues.Add("write_iops_sec_max", strconv.Itoa(iotune["write_iops_sec_max"].(int)))
_, err := c.DecortAPICall(ctx, "POST", disksIOLimitAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
log.Debugf("resourceDiskCreate: new Disk ID / name %d / %s creation sequence complete", diskId, d.Get("name").(string))
urlValues = &url.Values{}
}
// We may reuse dataSourceDiskRead here as we maintain similarity
// between Disk resource and Disk data source schemas
// Disk resource read function will also update resource ID on success, so that Terraform
// will know the resource exists (however, we already did it a few lines before)
return dataSourceDiskRead(ctx, d, m)
dgn := resourceDiskRead(ctx, d, m)
if dgn != nil {
return dgn
}
return nil
}
func resourceDiskRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
diskFacts, err := utilityDiskCheckPresence(ctx, d, m)
if diskFacts == "" {
// if empty string is returned from utilityDiskCheckPresence then there is no
// such Disk and err tells so - just return it to the calling party
d.SetId("") // ensure ID is empty
disk, err := utilityDiskCheckPresence(ctx, d, m)
if disk == nil {
d.SetId("")
if err != nil {
return diag.FromErr(err)
}
return nil
}
diskAcl, _ := json.Marshal(disk.Acl)
d.Set("account_id", disk.AccountID)
d.Set("account_name", disk.AccountName)
d.Set("acl", string(diskAcl))
d.Set("boot_partition", disk.BootPartition)
d.Set("compute_id", disk.ComputeID)
d.Set("compute_name", disk.ComputeName)
d.Set("created_time", disk.CreatedTime)
d.Set("deleted_time", disk.DeletedTime)
d.Set("desc", disk.Desc)
d.Set("destruction_time", disk.DestructionTime)
d.Set("devicename", disk.DeviceName)
d.Set("disk_path", disk.DiskPath)
d.Set("gid", disk.GridID)
d.Set("guid", disk.GUID)
d.Set("disk_id", disk.ID)
d.Set("image_id", disk.ImageID)
d.Set("images", disk.Images)
d.Set("iotune", flattenIOTune(disk.IOTune))
d.Set("iqn", disk.IQN)
d.Set("login", disk.Login)
d.Set("milestones", disk.Milestones)
d.Set("disk_name", disk.Name)
d.Set("order", disk.Order)
d.Set("params", disk.Params)
d.Set("parent_id", disk.ParentId)
d.Set("passwd", disk.Passwd)
d.Set("pci_slot", disk.PciSlot)
d.Set("pool", disk.Pool)
d.Set("purge_attempts", disk.PurgeAttempts)
d.Set("purge_time", disk.PurgeTime)
d.Set("reality_device_number", disk.RealityDeviceNumber)
d.Set("reference_id", disk.ReferenceId)
d.Set("res_id", disk.ResID)
d.Set("res_name", disk.ResName)
d.Set("role", disk.Role)
d.Set("sep_id", disk.SepID)
d.Set("sep_type", disk.SepType)
d.Set("size_max", disk.SizeMax)
d.Set("size_used", disk.SizeUsed)
d.Set("snapshots", flattendDiskSnapshotList(disk.Snapshots))
d.Set("status", disk.Status)
d.Set("tech_status", disk.TechStatus)
d.Set("type", disk.Type)
d.Set("vmid", disk.VMID)
return diag.FromErr(flattenDisk(d, diskFacts))
return nil
}
func resourceDiskUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// Update will only change the following attributes of the disk:
// - Size; to keep data safe, shrinking disk is not allowed.
// - Name
//
// Attempt to change disk type will throw an error and mark disk
// resource as partially updated
log.Debugf("resourceDiskUpdate: called for Disk ID / name %s / %s, Account ID %d",
d.Id(), d.Get("name").(string), d.Get("account_id").(int))
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
oldSize, newSize := d.GetChange("size")
if d.HasChange("size_max") {
oldSize, newSize := d.GetChange("size_max")
if oldSize.(int) < newSize.(int) {
log.Debugf("resourceDiskUpdate: resizing disk ID %s - %d GB -> %d GB",
d.Id(), oldSize.(int), newSize.(int))
sizeParams := &url.Values{}
sizeParams.Add("diskId", d.Id())
sizeParams.Add("size", fmt.Sprintf("%d", newSize.(int)))
_, err := c.DecortAPICall(ctx, "POST", DisksResizeAPI, sizeParams)
urlValues.Add("diskId", d.Id())
urlValues.Add("size", fmt.Sprintf("%d", newSize.(int)))
_, err := c.DecortAPICall(ctx, "POST", disksResizeAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.Set("size", newSize)
d.Set("size_max", newSize)
} else if oldSize.(int) > newSize.(int) {
return diag.FromErr(fmt.Errorf("resourceDiskUpdate: Disk ID %s - reducing disk size is not allowed", d.Id()))
}
urlValues = &url.Values{}
}
oldName, newName := d.GetChange("name")
if oldName.(string) != newName.(string) {
log.Debugf("resourceDiskUpdate: renaming disk ID %d - %s -> %s",
d.Get("disk_id").(int), oldName.(string), newName.(string))
renameParams := &url.Values{}
renameParams.Add("diskId", d.Id())
renameParams.Add("name", newName.(string))
_, err := c.DecortAPICall(ctx, "POST", DisksRenameAPI, renameParams)
if d.HasChange("disk_name") {
urlValues.Add("diskId", d.Id())
urlValues.Add("name", d.Get("disk_name").(string))
_, err := c.DecortAPICall(ctx, "POST", disksRenameAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
/*
NOTE: plugin will manage disks of type "Data" only, and type cannot be changed once disk is created
if d.HasChange("iotune") {
iot := d.Get("iotune").([]interface{})[0]
iotune := iot.(map[string]interface{})
urlValues.Add("diskId", d.Id())
urlValues.Add("iops", strconv.Itoa(iotune["total_iops_sec"].(int)))
urlValues.Add("read_bytes_sec", strconv.Itoa(iotune["read_bytes_sec"].(int)))
urlValues.Add("read_bytes_sec_max", strconv.Itoa(iotune["read_bytes_sec_max"].(int)))
urlValues.Add("read_iops_sec", strconv.Itoa(iotune["read_iops_sec"].(int)))
urlValues.Add("read_iops_sec_max", strconv.Itoa(iotune["read_iops_sec_max"].(int)))
urlValues.Add("size_iops_sec", strconv.Itoa(iotune["size_iops_sec"].(int)))
urlValues.Add("total_bytes_sec", strconv.Itoa(iotune["total_bytes_sec"].(int)))
urlValues.Add("total_bytes_sec_max", strconv.Itoa(iotune["total_bytes_sec_max"].(int)))
urlValues.Add("total_iops_sec_max", strconv.Itoa(iotune["total_iops_sec_max"].(int)))
urlValues.Add("write_bytes_sec", strconv.Itoa(iotune["write_bytes_sec"].(int)))
urlValues.Add("write_bytes_sec_max", strconv.Itoa(iotune["write_bytes_sec_max"].(int)))
urlValues.Add("write_iops_sec", strconv.Itoa(iotune["write_iops_sec"].(int)))
urlValues.Add("write_iops_sec_max", strconv.Itoa(iotune["write_iops_sec_max"].(int)))
_, err := c.DecortAPICall(ctx, "POST", disksIOLimitAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
oldType, newType := d.GetChange("type")
if oldType.(string) != newType.(string) {
return fmt.Errorf("resourceDiskUpdate: Disk ID %s - changing type of existing disk not allowed", d.Id())
urlValues = &url.Values{}
}
*/
// we may reuse dataSourceDiskRead here as we maintain similarity
// between Compute resource and Compute data source schemas
return dataSourceDiskRead(ctx, d, m)
if d.HasChange("restore") {
if d.Get("restore").(bool) {
urlValues.Add("diskId", d.Id())
urlValues.Add("reason", d.Get("reason").(string))
_, err := c.DecortAPICall(ctx, "POST", disksRestoreAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
}
return resourceDiskRead(ctx, d, m)
}
func resourceDiskDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// NOTE: this function tries to detach and destroy target Disk "permanently", so
// there is no way to restore it.
// If, however, the disk is attached to a compute, the method will
// fail (by failing the underpinning DECORt API call, which is issued with detach=false)
log.Debugf("resourceDiskDelete: called for Disk ID / name %d / %s, Account ID %d",
d.Get("disk_id").(int), d.Get("name").(string), d.Get("account_id").(int))
diskFacts, err := utilityDiskCheckPresence(ctx, d, m)
if diskFacts == "" {
disk, err := utilityDiskCheckPresence(ctx, d, m)
if disk == nil {
if err != nil {
return diag.FromErr(err)
}
// the specified Disk does not exist - in this case according to Terraform best practice
// we exit from Destroy method without error
return nil
}
params := &url.Values{}
params.Add("diskId", d.Id())
// NOTE: we are not force-detaching disk from a compute (if attached) thus protecting
// data that may be on that disk from destruction.
// However, this may change in the future, as TF state management logic may want
// to delete disk resource BEFORE it is detached from compute instance, and, while
// perfectly OK from data preservation viewpoint, this is breaking expected TF workflow
// in the eyes of an experienced TF user
params.Add("detach", "0")
params.Add("permanently", "1")
params.Add("detach", strconv.FormatBool(d.Get("detach").(bool)))
params.Add("permanently", strconv.FormatBool(d.Get("permanently").(bool)))
params.Add("reason", d.Get("reason").(string))
c := m.(*controller.ControllerCfg)
_, err = c.DecortAPICall(ctx, "POST", DisksDeleteAPI, params)
_, err = c.DecortAPICall(ctx, "POST", disksDeleteAPI, params)
if err != nil {
return diag.FromErr(err)
}
@ -196,12 +282,9 @@ func resourceDiskDelete(ctx context.Context, d *schema.ResourceData, m interface
}
func resourceDiskExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
// Reminder: according to Terraform rules, this function should not modify its ResourceData argument
log.Debugf("resourceDiskExists: called for Disk ID / name %d / %s, Account ID %d",
d.Get("disk_id").(int), d.Get("name").(string), d.Get("account_id").(int))
diskFacts, err := utilityDiskCheckPresence(ctx, d, m)
if diskFacts == "" {
if diskFacts == nil {
if err != nil {
return false, err
}
@ -212,101 +295,318 @@ func resourceDiskExists(ctx context.Context, d *schema.ResourceData, m interface
func resourceDiskSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"name": {
"account_id": {
Type: schema.TypeInt,
Required: true,
},
"disk_name": {
Type: schema.TypeString,
Required: true,
Description: "Name of this disk. NOTE: disk names are NOT unique within an account. If disk ID is specified, disk name is ignored.",
},
"disk_id": {
"size_max": {
Type: schema.TypeInt,
Optional: true,
Description: "ID of the disk to get. If disk ID is specified, then disk name and account ID are ignored.",
Required: true,
},
"account_id": {
"gid": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the account this disk belongs to.",
},
"pool": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"sep_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
Description: "Storage end-point provider serving this disk. Cannot be changed for existing disk.",
},
"pool": {
"desc": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Description: "Pool where this disk is located. Cannot be changed for existing disk.",
},
"size": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntAtLeast(1),
Description: "Size of the disk in GB. Note, that existing disks can only be grown in size.",
},
/* We moved "type" attribute to computed attributes section, as plugin manages disks of only
one type - "D", e.g. data disks.
"type": {
Type: schema.TypeString,
Optional: true,
Default: "D",
StateFunc: stateFuncToUpper,
ValidateFunc: validation.StringInSlice([]string{"B", "D"}, false),
Description: "Optional type of this disk. Defaults to D, i.e. data disk. Cannot be changed for existing disks.",
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"D", "B", "T"}, false),
},
*/
"description": {
"detach": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "detach disk from machine first",
},
"permanently": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "whether to completely delete the disk, works only with non attached disks",
},
"reason": {
Type: schema.TypeString,
Optional: true,
Default: "Disk resource managed by Terraform",
Description: "Optional user-defined text description of this disk.",
Default: "",
Description: "reason for an action",
},
"restore": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "restore deleting disk",
},
// The rest of the attributes are all computed
"disk_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the account this disk belongs to.",
},
"acl": {
Type: schema.TypeString,
Computed: true,
},
"boot_partition": {
Type: schema.TypeInt,
Computed: true,
},
"compute_id": {
Type: schema.TypeInt,
Computed: true,
},
"compute_name": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"destruction_time": {
Type: schema.TypeInt,
Computed: true,
},
"devicename": {
Type: schema.TypeString,
Computed: true,
},
"disk_path": {
Type: schema.TypeString,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"image_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the image, which this disk was cloned from (if ever cloned).",
},
"images": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"iotune": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"read_bytes_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"read_bytes_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"read_iops_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"read_iops_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"size_iops_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"total_bytes_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"total_bytes_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"total_iops_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"total_iops_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"write_bytes_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"write_bytes_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"write_iops_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"write_iops_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
},
},
},
"iqn": {
Type: schema.TypeString,
Computed: true,
},
"login": {
Type: schema.TypeString,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"type": {
"order": {
Type: schema.TypeInt,
Computed: true,
},
"params": {
Type: schema.TypeString,
Computed: true,
},
"parent_id": {
Type: schema.TypeInt,
Computed: true,
},
"passwd": {
Type: schema.TypeString,
Computed: true,
Description: "Type of this disk.",
},
"pci_slot": {
Type: schema.TypeInt,
Computed: true,
},
"sep_type": {
"purge_attempts": {
Type: schema.TypeInt,
Computed: true,
},
"purge_time": {
Type: schema.TypeInt,
Computed: true,
},
"reality_device_number": {
Type: schema.TypeInt,
Computed: true,
},
"reference_id": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"res_name": {
Type: schema.TypeString,
Computed: true,
},
"role": {
Type: schema.TypeString,
Computed: true,
Description: "Type of the storage end-point provider serving this disk.",
},
/*
"sep_type": {
Type: schema.TypeString,
Computed: true,
},
"size_used": {
Type: schema.TypeInt,
Computed: true,
},
"snapshots": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource {
Schema: snapshotSubresourceSchemaMake(),
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Computed: true,
},
"label": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_guid": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_time": {
Type: schema.TypeInt,
Computed: true,
},
Description: "List of user-created snapshots for this disk."
"timestamp": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"vmid": {
Type: schema.TypeInt,
Computed: true,
},
*/
}
return rets

@ -34,7 +34,6 @@ package disks
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
@ -44,103 +43,28 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityDiskCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (string, error) {
// This function tries to locate Disk by one of the following algorithms depending on
// the parameters passed:
// - if disk ID is specified -> by disk ID
// - if disk name is specifeid -> by disk name and either account ID or account name
//
// NOTE: disk names are not unique, so the first occurence of this name in the account will
// be returned. There is no such ambiguity when locating disk by its ID.
//
// If succeeded, it returns non empty string that contains JSON formatted facts about the disk
// as returned by disks/get API call.
// Otherwise it returns empty string and meaningful error.
//
// This function does not modify its ResourceData argument, so it is safe to use it as core
// method for resource's Exists method.
//
func utilityDiskCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*Disk, error) {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
// make it possible to use "read" & "check presence" functions with disk ID set so
// that Import of preexisting Disk resource is possible
idSet := false
theId, err := strconv.Atoi(d.Id())
if err != nil || theId <= 0 {
diskId, argSet := d.GetOk("disk_id")
if argSet {
theId = diskId.(int)
idSet = true
}
} else {
idSet = true
}
if idSet {
// disk ID is specified, try to get disk instance straight by this ID
log.Debugf("utilityDiskCheckPresence: locating disk by its ID %d", theId)
urlValues.Add("diskId", fmt.Sprintf("%d", theId))
diskFacts, err := c.DecortAPICall(ctx, "POST", DisksGetAPI, urlValues)
if err != nil {
return "", err
}
return diskFacts, nil
}
// ID or disk_di was not set in the schema upon entering this function - rely on Disk name
// and Account ID to find the disk
diskName, argSet := d.GetOk("name")
if !argSet {
// no disk ID and no disk name - we cannot locate disk in this case
return "", fmt.Errorf("Cannot locate disk if name is empty and no disk ID specified")
}
// Valid account ID is required to locate disks
// obtain Account ID by account name - it should not be zero on success
disk := &Disk{}
urlValues.Add("accountId", fmt.Sprintf("%d", d.Get("account_id").(int)))
diskFacts, err := c.DecortAPICall(ctx, "POST", DisksListAPI, urlValues)
if err != nil {
return "", err
if d.Get("disk_id").(int) == 0 {
urlValues.Add("diskId", d.Id())
} else {
urlValues.Add("diskId", strconv.Itoa(d.Get("disk_id").(int)))
}
log.Debugf("utilityDiskCheckPresence: ready to unmarshal string %s", diskFacts)
disksList := DisksListResp{}
err = json.Unmarshal([]byte(diskFacts), &disksList)
log.Debugf("utilityDiskCheckPresence: load disk")
diskRaw, err := c.DecortAPICall(ctx, "POST", disksGetAPI, urlValues)
if err != nil {
return "", err
return nil, err
}
// log.Printf("%#v", vm_list)
log.Debugf("utilityDiskCheckPresence: traversing decoded JSON of length %d", len(disksList))
for index, item := range disksList {
// need to match disk by name, return the first match
if item.Name == diskName.(string) && item.Status != "DESTROYED" {
log.Debugf("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
/*
// TODO: this may not be optimal as it initiates one extra call to the DECORT controller
// in spite of the fact that we already have all required information about the disk in
// item variable
//
get_urlValues := &url.Values{}
get_urlValues.Add("diskId", fmt.Sprintf("%d", item.ID))
diskFacts, err = controller.decortAPICall("POST", DisksGetAPI, get_urlValues)
err = json.Unmarshal([]byte(diskRaw), disk)
if err != nil {
return "", err
}
return diskFacts, nil
*/
reencodedItem, err := json.Marshal(item)
if err != nil {
return "", err
}
return string(reencodedItem[:]), nil
}
return nil, err
}
return "", nil // there should be no error if disk does not exist
return disk, nil
}

@ -44,8 +44,8 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityDiskListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (DisksListResp, error) {
diskList := DisksListResp{}
func utilityDiskListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (DisksList, error) {
diskList := DisksList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
@ -62,8 +62,8 @@ func utilityDiskListCheckPresence(ctx context.Context, d *schema.ResourceData, m
urlValues.Add("accountId", strconv.Itoa(accountId.(int)))
}
log.Debugf("utilityDiskListCheckPresence: load grid list")
diskListRaw, err := c.DecortAPICall(ctx, "POST", DisksListAPI, urlValues)
log.Debugf("utilityDiskListCheckPresence: load disk list")
diskListRaw, err := c.DecortAPICall(ctx, "POST", disksListAPI, urlValues)
if err != nil {
return nil, err
}

@ -0,0 +1,40 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
const imageCreateAPI = "/restmachine/cloudapi/image/createImage"
const imageCreateVirtualAPI = "/restmachine/cloudapi/image/createVirtual"
const imageGetAPI = "/restmachine/cloudapi/image/get"
const imageListGetAPI = "/restmachine/cloudapi/image/list"
const imageDeleteAPI = "/restmachine/cloudapi/image/delete"
const imageEditNameAPI = "/restmachine/cloudapi/image/rename"
const imageLinkAPI = "/restmachine/cloudapi/image/link"

@ -0,0 +1,123 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenHistory(history []History) []map[string]interface{} {
temp := make([]map[string]interface{}, 0)
for _, item := range history {
t := map[string]interface{}{
"id": item.Id,
"guid": item.Guid,
"timestamp": item.Timestamp,
}
temp = append(temp, t)
}
return temp
}
func flattenImage(d *schema.ResourceData, img *ImageExtend) {
d.Set("unc_path", img.UNCPath)
d.Set("ckey", img.CKey)
d.Set("account_id", img.AccountId)
d.Set("acl", img.Acl)
d.Set("architecture", img.Architecture)
d.Set("boot_type", img.BootType)
d.Set("bootable", img.Bootable)
d.Set("compute_ci_id", img.ComputeCiId)
d.Set("deleted_time", img.DeletedTime)
d.Set("desc", img.Description)
d.Set("drivers", img.Drivers)
d.Set("enabled", img.Enabled)
d.Set("gid", img.GridId)
d.Set("guid", img.GUID)
d.Set("history", flattenHistory(img.History))
d.Set("hot_resize", img.HotResize)
d.Set("image_id", img.Id)
d.Set("last_modified", img.LastModified)
d.Set("link_to", img.LinkTo)
d.Set("milestones", img.Milestones)
d.Set("image_name", img.Name)
d.Set("password", img.Password)
d.Set("pool_name", img.Pool)
d.Set("provider_name", img.ProviderName)
d.Set("purge_attempts", img.PurgeAttempts)
d.Set("res_id", img.ResId)
d.Set("rescuecd", img.RescueCD)
d.Set("sep_id", img.SepId)
d.Set("shared_with", img.SharedWith)
d.Set("size", img.Size)
d.Set("status", img.Status)
d.Set("tech_status", img.TechStatus)
d.Set("type", img.Type)
d.Set("username", img.Username)
d.Set("version", img.Version)
}
func dataSourceImageRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
image, err := utilityImageCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
flattenImage(d, image)
return nil
}
func DataSourceImage() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceImageRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceImageExtendSchemaMake(),
}
}

@ -0,0 +1,126 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenImageList(il ImageList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, img := range il {
temp := map[string]interface{}{
"account_id": img.AccountId,
"architecture": img.Architecture,
"boot_type": img.BootType,
"bootable": img.Bootable,
"cdrom": img.CDROM,
"desc": img.Description,
"drivers": img.Drivers,
"hot_resize": img.HotResize,
"image_id": img.Id,
"link_to": img.LinkTo,
"image_name": img.Name,
"pool_name": img.Pool,
"sep_id": img.SepId,
"size": img.Size,
"status": img.Status,
"type": img.Type,
"username": img.Username,
"virtual": img.Virtual,
}
res = append(res, temp)
}
return res
}
func dataSourceImageListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
imageList, err := utilityImageListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenImageList(imageList))
return nil
}
func dataSourceImageListSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Optional: true,
Description: "optional account ID to include account images",
},
"page": {
Type: schema.TypeInt,
Optional: true,
Description: "page number",
},
"size": {
Type: schema.TypeInt,
Optional: true,
Description: "page size",
},
"items": {
Type: schema.TypeList,
Computed: true,
Description: "image list",
Elem: &schema.Resource{
Schema: dataSourceImageSchemaMake(),
},
},
}
return rets
}
func DataSourceImageList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceImageListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceImageListSchemaMake(),
}
}

@ -0,0 +1,208 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceImageExtendSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"image_id": {
Type: schema.TypeInt,
Required: true,
},
"show_all": {
Type: schema.TypeBool,
Default: false,
Optional: true,
},
"unc_path": {
Type: schema.TypeString,
Computed: true,
},
"ckey": {
Type: schema.TypeString,
Computed: true,
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"acl": {
Type: schema.TypeString,
Computed: true,
},
"architecture": {
Type: schema.TypeString,
Computed: true,
},
"boot_type": {
Type: schema.TypeString,
Computed: true,
},
"bootable": {
Type: schema.TypeBool,
Computed: true,
},
"compute_ci_id": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_time": {
Type: schema.TypeString,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Computed: true,
},
"drivers": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"enabled": {
Type: schema.TypeBool,
Computed: true,
},
"gid": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"history": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Computed: true,
},
"id": {
Type: schema.TypeInt,
Computed: true,
},
"timestamp": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"hot_resize": {
Type: schema.TypeBool,
Computed: true,
},
"last_modified": {
Type: schema.TypeInt,
Computed: true,
},
"link_to": {
Type: schema.TypeInt,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"image_name": {
Type: schema.TypeString,
Computed: true,
},
"password": {
Type: schema.TypeString,
Computed: true,
},
"pool_name": {
Type: schema.TypeString,
Computed: true,
},
"provider_name": {
Type: schema.TypeString,
Computed: true,
},
"purge_attempts": {
Type: schema.TypeInt,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"rescuecd": {
Type: schema.TypeBool,
Computed: true,
},
"sep_id": {
Type: schema.TypeInt,
Computed: true,
},
"shared_with": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"size": {
Type: schema.TypeInt,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"username": {
Type: schema.TypeString,
Computed: true,
},
"version": {
Type: schema.TypeString,
Computed: true,
},
}
}

@ -0,0 +1,132 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceImageSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
Description: "Owner account id",
},
"architecture": {
Type: schema.TypeString,
Computed: true,
Description: "Image architecture",
},
"boot_type": {
Type: schema.TypeString,
Computed: true,
Description: "Boot image type",
},
"bootable": {
Type: schema.TypeBool,
Computed: true,
Description: "Flag, true if image is bootable, otherwise - false",
},
"cdrom": {
Type: schema.TypeBool,
Computed: true,
Description: "Flag, true if image is cdrom image, otherwise - false",
},
"desc": {
Type: schema.TypeString,
Computed: true,
Description: "Image description",
},
"drivers": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "Image drivers",
},
"hot_resize": {
Type: schema.TypeBool,
Computed: true,
Description: "Flag, true if image supports hot resize, else if not",
},
"image_id": {
Type: schema.TypeInt,
Computed: true,
Description: "Image id",
},
"link_to": {
Type: schema.TypeInt,
Computed: true,
Description: "For virtual images, id image, which current image linked",
},
"image_name": {
Type: schema.TypeString,
Computed: true,
Description: "Image name",
},
"pool_name": {
Type: schema.TypeString,
Computed: true,
Description: "Image pool",
},
"sep_id": {
Type: schema.TypeInt,
Computed: true,
Description: "Image storage endpoint id",
},
"size": {
Type: schema.TypeInt,
Computed: true,
Description: "Image size",
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Image status",
},
"type": {
Type: schema.TypeString,
Computed: true,
Description: "Image type",
},
"username": {
Type: schema.TypeString,
Computed: true,
Description: "username",
},
"virtual": {
Type: schema.TypeBool,
Computed: true,
Description: "True if image is virtula, otherwise - else",
},
}
}

@ -0,0 +1,158 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func resourceImageSchemaMake(sch map[string]*schema.Schema) map[string]*schema.Schema {
delete(sch, "show_all")
sch["name"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "Name of the rescue disk",
}
sch["url"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "URL where to download media from",
}
sch["gid"] = &schema.Schema{
Type: schema.TypeInt,
Required: true,
Description: "grid (platform) ID where this template should be create in",
}
sch["image_id"] = &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "image id",
}
sch["boot_type"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"bios", "uefi"}, true),
Description: "Boot type of image bios or uefi",
}
sch["type"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"linux", "windows", "other"}, true),
Description: "Image type linux, windows or other",
}
sch["hot_resize"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Computed: true,
Description: "Does this machine supports hot resize",
}
sch["username"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Optional username for the image",
}
sch["password"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Optional password for the image",
}
sch["account_id"] = &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "AccountId to make the image exclusive",
}
sch["username_dl"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "username for upload binary media",
}
sch["password_dl"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "password for upload binary media",
}
sch["pool_name"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "pool for image create",
}
sch["sep_id"] = &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "storage endpoint provider ID",
}
sch["architecture"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"X86_64", "PPC64_LE"}, true),
Description: "binary architecture of this image, one of X86_64 of PPC64_LE",
}
sch["drivers"] = &schema.Schema{
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
}
sch["permanently"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "whether to completely delete the image",
}
return sch
}

@ -0,0 +1,66 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func resourceImageVirtualSchemaMake(sch map[string]*schema.Schema) map[string]*schema.Schema {
delete(sch, "show_all")
sch["name"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "Name of the rescue disk",
}
sch["link_to"] = &schema.Schema{
Type: schema.TypeInt,
Required: true,
Description: "ID of real image to link this virtual image to upon creation",
}
sch["permanently"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "whether to completely delete the image",
}
sch["image_id"] = &schema.Schema{
Type: schema.TypeInt,
Computed: true,
Description: "Image id",
}
return sch
}

@ -0,0 +1,148 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
/*
type History struct {
Guid string `json:"guid"`
Id int `json:"id"`
Timestamp int64 `json:"timestamp"`
}
type Image struct {
ImageId int `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
Gid int `json:"gid"`
Guid int `json:"guid"`
Boottype string `json:"bootType"`
Imagetype string `json:"type"`
Drivers []string `json:"drivers"`
Hotresize bool `json:"hotResize"`
Bootable bool `json:"bootable"`
Username string `json:"username"`
Password string `json:"password"`
AccountId int `json:"accountId"`
UsernameDL string `json:"usernameDL"`
PasswordDL string `json:"passwordDL"`
SepId int `json:"sepId"`
PoolName string `json:"pool"`
Architecture string `json:"architecture"`
UNCPath string `json:"UNCPath"`
LinkTo int `json:"linkTo"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
Size int `json:"size"`
Version string `json:"version"`
Enabled bool `json:"enabled"`
ComputeciId int `json:"computeciId"`
Milestones int `json:"milestones"`
ProviderName string `json:"provider_name"`
PurgeAttempts int `json:"purgeAttempts"`
ReferenceId string `json:"referenceId"`
ResId string `json:"resId"`
ResName string `json:"resName"`
Rescuecd bool `json:"rescuecd"`
Meta []interface{} `json:"_meta"`
History []History `json:"history"`
LastModified int64 `json:"lastModified"`
Desc string `json:"desc"`
SharedWith []int `json:"sharedWith"`
}
*/
type Image struct {
AccountId int `json:"accountId"`
Architecture string `json:"architecture"`
BootType string `json:"bootType"`
Bootable bool `json:"bootable"`
CDROM bool `json:"cdrom"`
Description string `json:"desc"`
Drivers []string `json:"drivers"`
HotResize bool `json:"hotResize"`
Id int `json:"id"`
LinkTo int `json:"linkTo"`
Name string `json:"name"`
Pool string `json:"pool"`
SepId int `json:"sepId"`
Size int `json:"size"`
Status string `json:"status"`
Type string `json:"type"`
Username string `json:"username"`
Virtual bool `json:"virtual"`
}
type ImageList []Image
type History struct {
Guid string `json:"guid"`
Id int `json:"id"`
Timestamp int64 `json:"timestamp"`
}
type ImageExtend struct {
UNCPath string `json:"UNCPath"`
CKey string `json:"_ckey"`
AccountId int `json:"accountId"`
Acl interface{} `json:"acl"`
Architecture string `json:"architecture"`
BootType string `json:"bootType"`
Bootable bool `json:"bootable"`
ComputeCiId int `json:"computeciId"`
DeletedTime int `json:"deletedTime"`
Description string `json:"desc"`
Drivers []string `json:"drivers"`
Enabled bool `json:"enabled"`
GridId int `json:"gid"`
GUID int `json:"guid"`
History []History `json:"history"`
HotResize bool `json:"hotResize"`
Id int `json:"id"`
LastModified int `json:"lastModified"`
LinkTo int `json:"linkTo"`
Milestones int `json:"milestones"`
Name string `json:"name"`
Password string `json:"password"`
Pool string `json:"pool"`
ProviderName string `json:"provider_name"`
PurgeAttempts int `json:"purgeAttempts"`
ResId string `json:"resId"`
RescueCD bool `json:"rescuecd"`
SepId int `json:"sepId"`
SharedWith []int `json:"sharedWith"`
Size int `json:"size"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
Type string `json:"type"`
Username string `json:"username"`
Version string `json:"version"`
}

@ -0,0 +1,259 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import (
"context"
"net/url"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
)
func resourceImageCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceImageCreate: called for image %s", d.Get("name").(string))
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("name", d.Get("name").(string))
urlValues.Add("url", d.Get("url").(string))
urlValues.Add("gid", strconv.Itoa(d.Get("gid").(int)))
urlValues.Add("boottype", d.Get("boot_type").(string))
urlValues.Add("imagetype", d.Get("image_type").(string))
tstr := d.Get("drivers").([]interface{})
temp := ""
l := len(tstr)
for i, str := range tstr {
s := "\"" + str.(string) + "\""
if i != (l - 1) {
s += ","
}
temp = temp + s
}
temp = "[" + temp + "]"
urlValues.Add("drivers", temp)
if hotresize, ok := d.GetOk("hot_resize"); ok {
urlValues.Add("hotresize", strconv.FormatBool(hotresize.(bool)))
}
if username, ok := d.GetOk("username"); ok {
urlValues.Add("username", username.(string))
}
if password, ok := d.GetOk("password"); ok {
urlValues.Add("password", password.(string))
}
if accountId, ok := d.GetOk("account_id"); ok {
urlValues.Add("accountId", strconv.Itoa(accountId.(int)))
}
if usernameDL, ok := d.GetOk("username_dl"); ok {
urlValues.Add("usernameDL", usernameDL.(string))
}
if passwordDL, ok := d.GetOk("password_dl"); ok {
urlValues.Add("passwordDL", passwordDL.(string))
}
if sepId, ok := d.GetOk("sep_id"); ok {
urlValues.Add("sepId", strconv.Itoa(sepId.(int)))
}
if poolName, ok := d.GetOk("pool_name"); ok {
urlValues.Add("poolName", poolName.(string))
}
if architecture, ok := d.GetOk("architecture"); ok {
urlValues.Add("architecture", architecture.(string))
}
imageId, err := c.DecortAPICall(ctx, "POST", imageCreateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId(imageId)
d.Set("image_id", imageId)
_, err = utilityImageCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
diagnostics := resourceImageRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
return nil
}
func resourceImageRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceImageRead: called for %s id: %s", d.Get("name").(string), d.Id())
img, err := utilityImageCheckPresence(ctx, d, m)
if img == nil {
d.SetId("")
return diag.FromErr(err)
}
d.Set("unc_path", img.UNCPath)
d.Set("ckey", img.CKey)
d.Set("account_id", img.AccountId)
d.Set("acl", img.Acl)
d.Set("architecture", img.Architecture)
d.Set("boot_type", img.BootType)
d.Set("bootable", img.Bootable)
d.Set("compute_ci_id", img.ComputeCiId)
d.Set("deleted_time", img.DeletedTime)
d.Set("desc", img.Description)
d.Set("drivers", img.Drivers)
d.Set("enabled", img.Enabled)
d.Set("gid", img.GridId)
d.Set("guid", img.GUID)
d.Set("history", flattenHistory(img.History))
d.Set("hot_resize", img.HotResize)
d.Set("image_id", img.Id)
d.Set("last_modified", img.LastModified)
d.Set("link_to", img.LinkTo)
d.Set("milestones", img.Milestones)
d.Set("image_name", img.Name)
d.Set("password", img.Password)
d.Set("pool_name", img.Pool)
d.Set("provider_name", img.ProviderName)
d.Set("purge_attempts", img.PurgeAttempts)
d.Set("res_id", img.ResId)
d.Set("rescuecd", img.RescueCD)
d.Set("sep_id", img.SepId)
d.Set("shared_with", img.SharedWith)
d.Set("size", img.Size)
d.Set("status", img.Status)
d.Set("tech_status", img.TechStatus)
d.Set("type", img.Type)
d.Set("username", img.Username)
d.Set("version", img.Version)
return nil
}
func resourceImageDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceImageDelete: called for %s, id: %s", d.Get("name").(string), d.Id())
image, err := utilityImageCheckPresence(ctx, d, m)
if image == nil {
if err != nil {
return diag.FromErr(err)
}
return nil
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int)))
if permanently, ok := d.GetOk("permanently"); ok {
urlValues.Add("permanently", strconv.FormatBool(permanently.(bool)))
}
_, err = c.DecortAPICall(ctx, "POST", imageDeleteAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId("")
return nil
}
func resourceImageExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
log.Debugf("resourceImageExists: called for %s, id: %s", d.Get("name").(string), d.Id())
image, err := utilityImageCheckPresence(ctx, d, m)
if image == nil {
if err != nil {
return false, err
}
return false, nil
}
return true, nil
}
func resourceImageEditName(ctx context.Context, d *schema.ResourceData, m interface{}) error {
log.Debugf("resourceImageEditName: called for %s, id: %s", d.Get("name").(string), d.Id())
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int)))
urlValues.Add("name", d.Get("name").(string))
_, err := c.DecortAPICall(ctx, "POST", imageEditNameAPI, urlValues)
if err != nil {
return err
}
return nil
}
func resourceImageEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceImageEdit: called for %s, id: %s", d.Get("name").(string), d.Id())
if d.HasChange("name") {
err := resourceImageEditName(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
}
return resourceImageRead(ctx, d, m)
}
func ResourceImage() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceImageCreate,
ReadContext: resourceImageRead,
UpdateContext: resourceImageEdit,
DeleteContext: resourceImageDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout60s,
Read: &constants.Timeout30s,
Update: &constants.Timeout60s,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: resourceImageSchemaMake(dataSourceImageExtendSchemaMake()),
}
}

@ -0,0 +1,132 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import (
"context"
"net/url"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
)
func resourceImageVirtualCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceImageVirtualCreate: called for image %s", d.Get("name").(string))
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("name", d.Get("name").(string))
urlValues.Add("targetId", strconv.Itoa(d.Get("target_id").(int)))
imageId, err := c.DecortAPICall(ctx, "POST", imageCreateVirtualAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId(imageId)
d.Set("image_id", imageId)
_, err = utilityImageCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
diagnostics := resourceImageRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
return nil
}
func resourceImageVirtualEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceImageEdit: called for %s, id: %s", d.Get("name").(string), d.Id())
if d.HasChange("name") {
err := resourceImageEditName(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
}
if d.HasChange("link_to") {
err := resourceImageVirtualLink(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
}
return resourceImageRead(ctx, d, m)
}
func resourceImageVirtualLink(ctx context.Context, d *schema.ResourceData, m interface{}) error {
log.Debugf("resourceVirtualImageLink: called for %s, id: %s", d.Get("name").(string), d.Id())
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int)))
urlValues.Add("targetId", strconv.Itoa(d.Get("link_to").(int)))
_, err := c.DecortAPICall(ctx, "POST", imageLinkAPI, urlValues)
if err != nil {
return err
}
return nil
}
func ResourceImageVirtual() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceImageVirtualCreate,
ReadContext: resourceImageRead,
UpdateContext: resourceImageVirtualEdit,
DeleteContext: resourceImageDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout60s,
Read: &constants.Timeout30s,
Update: &constants.Timeout60s,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: resourceImageVirtualSchemaMake(dataSourceImageExtendSchemaMake()),
}
}

@ -0,0 +1,75 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/controller"
)
func utilityImageCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*ImageExtend, error) {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
if (strconv.Itoa(d.Get("image_id").(int))) != "0" {
urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int)))
} else {
urlValues.Add("imageId", d.Id())
}
if showAll, ok := d.GetOk("show_all"); ok {
urlValues.Add("page", strconv.FormatBool(showAll.(bool)))
}
resp, err := c.DecortAPICall(ctx, "POST", imageGetAPI, urlValues)
if err != nil {
return nil, err
}
if resp == "" {
return nil, nil
}
image := &ImageExtend{}
if err := json.Unmarshal([]byte(resp), image); err != nil {
return nil, errors.New(fmt.Sprint("Can not unmarshall data to image: ", resp, " ", image))
}
return image, nil
}

@ -0,0 +1,74 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package image
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityImageListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (ImageList, error) {
imageList := ImageList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
if accountId, ok := d.GetOk("account_id"); ok {
urlValues.Add("accountId", strconv.Itoa(accountId.(int)))
}
if page, ok := d.GetOk("page"); ok {
urlValues.Add("page", strconv.Itoa(page.(int)))
}
if size, ok := d.GetOk("size"); ok {
urlValues.Add("size", strconv.Itoa(size.(int)))
}
log.Debugf("utilityImageListCheckPresence: load image list")
imageListRaw, err := c.DecortAPICall(ctx, "POST", imageListGetAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(imageListRaw), &imageList)
if err != nil {
return nil, err
}
return imageList, nil
}

@ -0,0 +1,59 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceAccountAuditSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"call": {
Type: schema.TypeString,
Computed: true,
},
"responsetime": {
Type: schema.TypeFloat,
Computed: true,
},
"statuscode": {
Type: schema.TypeInt,
Computed: true,
},
"timestamp": {
Type: schema.TypeFloat,
Computed: true,
},
"user": {
Type: schema.TypeString,
Computed: true,
},
}
}

@ -0,0 +1,119 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceAccountComputeSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"cpus": {
Type: schema.TypeInt,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"compute_id": {
Type: schema.TypeInt,
Computed: true,
},
"compute_name": {
Type: schema.TypeString,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
"registered": {
Type: schema.TypeBool,
Computed: true,
},
"rg_id": {
Type: schema.TypeInt,
Computed: true,
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"total_disks_size": {
Type: schema.TypeInt,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"user_managed": {
Type: schema.TypeBool,
Computed: true,
},
"vins_connected": {
Type: schema.TypeInt,
Computed: true,
},
}
}

@ -0,0 +1,63 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceAccountDiskSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"disk_id": {
Type: schema.TypeInt,
Computed: true,
},
"disk_name": {
Type: schema.TypeString,
Computed: true,
},
"pool_name": {
Type: schema.TypeString,
Computed: true,
},
"sep_id": {
Type: schema.TypeInt,
Computed: true,
},
"size_max": {
Type: schema.TypeInt,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
}
}

@ -0,0 +1,262 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceAccountSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
},
"dc_location": {
Type: schema.TypeString,
Computed: true,
},
"resources": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"current": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"disksize": {
Type: schema.TypeInt,
Computed: true,
},
"extips": {
Type: schema.TypeInt,
Computed: true,
},
"exttraffic": {
Type: schema.TypeInt,
Computed: true,
},
"gpu": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"reserved": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"disksize": {
Type: schema.TypeInt,
Computed: true,
},
"extips": {
Type: schema.TypeInt,
Computed: true,
},
"exttraffic": {
Type: schema.TypeInt,
Computed: true,
},
"gpu": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
},
},
},
"ckey": {
Type: schema.TypeString,
Computed: true,
},
"meta": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"explicit": {
Type: schema.TypeBool,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"right": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"user_group_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"company": {
Type: schema.TypeString,
Computed: true,
},
"companyurl": {
Type: schema.TypeString,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deactivation_time": {
Type: schema.TypeFloat,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"displayname": {
Type: schema.TypeString,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"resource_limits": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cu_c": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_d": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_i": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_m": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_np": {
Type: schema.TypeFloat,
Computed: true,
},
"gpu_units": {
Type: schema.TypeFloat,
Computed: true,
},
},
},
},
"send_access_emails": {
Type: schema.TypeBool,
Computed: true,
},
"service_account": {
Type: schema.TypeBool,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"version": {
Type: schema.TypeInt,
Computed: true,
},
"vins": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
}
return res
}

@ -0,0 +1,123 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceAccountFlipGroupSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"client_type": {
Type: schema.TypeString,
Computed: true,
},
"conn_type": {
Type: schema.TypeString,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"default_gw": {
Type: schema.TypeString,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Computed: true,
},
"gid": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"fg_id": {
Type: schema.TypeInt,
Computed: true,
},
"ip": {
Type: schema.TypeString,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"fg_name": {
Type: schema.TypeString,
Computed: true,
},
"net_id": {
Type: schema.TypeInt,
Computed: true,
},
"net_type": {
Type: schema.TypeString,
Computed: true,
},
"netmask": {
Type: schema.TypeInt,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
}
}

@ -0,0 +1,189 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceAccountItemSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"dc_location": {
Type: schema.TypeString,
Computed: true,
},
"ckey": {
Type: schema.TypeString,
Computed: true,
},
"meta": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"explicit": {
Type: schema.TypeBool,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"right": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"user_group_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"company": {
Type: schema.TypeString,
Computed: true,
},
"companyurl": {
Type: schema.TypeString,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deactivation_time": {
Type: schema.TypeFloat,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"displayname": {
Type: schema.TypeString,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"resource_limits": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cu_c": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_d": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_i": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_m": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_np": {
Type: schema.TypeFloat,
Computed: true,
},
"gpu_units": {
Type: schema.TypeFloat,
Computed: true,
},
},
},
},
"send_access_emails": {
Type: schema.TypeBool,
Computed: true,
},
"service_account": {
Type: schema.TypeBool,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"version": {
Type: schema.TypeInt,
Computed: true,
},
"vins": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
}
}

@ -0,0 +1,205 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceAccountRGSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"computes": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"started": {
Type: schema.TypeInt,
Computed: true,
},
"stopped": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"resources": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"consumed": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"disksize": {
Type: schema.TypeInt,
Computed: true,
},
"extips": {
Type: schema.TypeInt,
Computed: true,
},
"exttraffic": {
Type: schema.TypeInt,
Computed: true,
},
"gpu": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"limits": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"disksize": {
Type: schema.TypeInt,
Computed: true,
},
"extips": {
Type: schema.TypeInt,
Computed: true,
},
"exttraffic": {
Type: schema.TypeInt,
Computed: true,
},
"gpu": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"reserved": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"disksize": {
Type: schema.TypeInt,
Computed: true,
},
"extips": {
Type: schema.TypeInt,
Computed: true,
},
"exttraffic": {
Type: schema.TypeInt,
Computed: true,
},
"gpu": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
},
},
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"rg_id": {
Type: schema.TypeInt,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"vinses": {
Type: schema.TypeInt,
Computed: true,
},
}
}

@ -0,0 +1,320 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func resourceAccountSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"account_name": {
Type: schema.TypeString,
Required: true,
Description: "account name",
},
"username": {
Type: schema.TypeString,
Required: true,
Description: "username of owner the account",
},
"emailaddress": {
Type: schema.TypeString,
Optional: true,
Description: "email",
},
"send_access_emails": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "if true send emails when a user is granted access to resources",
},
"users": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"user_id": {
Type: schema.TypeString,
Required: true,
},
"access_type": {
Type: schema.TypeString,
Required: true,
},
"recursive_delete": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
},
},
"restore": {
Type: schema.TypeBool,
Optional: true,
Description: "restore a deleted account",
},
"permanently": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "whether to completely delete the account",
},
"enable": {
Type: schema.TypeBool,
Optional: true,
Description: "enable/disable account",
},
"resource_limits": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cu_c": {
Type: schema.TypeFloat,
Optional: true,
Computed: true,
},
"cu_d": {
Type: schema.TypeFloat,
Optional: true,
Computed: true,
},
"cu_i": {
Type: schema.TypeFloat,
Optional: true,
Computed: true,
},
"cu_m": {
Type: schema.TypeFloat,
Optional: true,
Computed: true,
},
"cu_np": {
Type: schema.TypeFloat,
Optional: true,
Computed: true,
},
"gpu_units": {
Type: schema.TypeFloat,
Optional: true,
Computed: true,
},
},
},
},
"account_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"dc_location": {
Type: schema.TypeString,
Computed: true,
},
"resources": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"current": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"disksize": {
Type: schema.TypeInt,
Computed: true,
},
"extips": {
Type: schema.TypeInt,
Computed: true,
},
"exttraffic": {
Type: schema.TypeInt,
Computed: true,
},
"gpu": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"reserved": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"disksize": {
Type: schema.TypeInt,
Computed: true,
},
"extips": {
Type: schema.TypeInt,
Computed: true,
},
"exttraffic": {
Type: schema.TypeInt,
Computed: true,
},
"gpu": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
},
},
},
"ckey": {
Type: schema.TypeString,
Computed: true,
},
"meta": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"explicit": {
Type: schema.TypeBool,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"right": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"user_group_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"company": {
Type: schema.TypeString,
Computed: true,
},
"companyurl": {
Type: schema.TypeString,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deactivation_time": {
Type: schema.TypeFloat,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"displayname": {
Type: schema.TypeString,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"service_account": {
Type: schema.TypeBool,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"version": {
Type: schema.TypeInt,
Computed: true,
},
"vins": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
}
}

@ -0,0 +1,107 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func dataSourceAccountVinsSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"computes": {
Type: schema.TypeInt,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"external_ip": {
Type: schema.TypeString,
Computed: true,
},
"vin_id": {
Type: schema.TypeInt,
Computed: true,
},
"vin_name": {
Type: schema.TypeString,
Computed: true,
},
"network": {
Type: schema.TypeString,
Computed: true,
},
"pri_vnf_dev_id": {
Type: schema.TypeInt,
Computed: true,
},
"rg_id": {
Type: schema.TypeInt,
Computed: true,
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
}
}

@ -0,0 +1,54 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
const accountAddUserAPI = "/restmachine/cloudbroker/account/addUser"
const accountAuditsAPI = "/restmachine/cloudbroker/account/audits"
const accountCreateAPI = "/restmachine/cloudbroker/account/create"
const accountDeleteAPI = "/restmachine/cloudbroker/account/delete"
const accountDeleteUserAPI = "/restmachine/cloudbroker/account/deleteUser"
const accountDisableAPI = "/restmachine/cloudbroker/account/disable"
const accountEnableAPI = "/restmachine/cloudbroker/account/enable"
const accountGetAPI = "/restmachine/cloudbroker/account/get"
const accountListAPI = "/restmachine/cloudbroker/account/list"
const accountListComputesAPI = "/restmachine/cloudbroker/account/listComputes"
const accountListDeletedAPI = "/restmachine/cloudbroker/account/listDeleted"
const accountListDisksAPI = "/restmachine/cloudbroker/account/listDisks"
const accountListFlipGroupsAPI = "/restmachine/cloudbroker/account/listFlipGroups"
const accountListRGAPI = "/restmachine/cloudbroker/account/listRG"
const accountListVinsAPI = "/restmachine/cloudbroker/account/listVins"
const accountRestoreAPI = "/restmachine/cloudbroker/account/restore"
const accountUpdateAPI = "/restmachine/cloudbroker/account/update"
const accountUpdateUserAPI = "/restmachine/cloudbroker/account/updateUser"
const accountsEnableAPI = "/restmachine/cloudbroker/account/enableAccounts"
const accountsDisableAPI = "/restmachine/cloudbroker/account/disableAccounts"
const accountsDeleteAPI = "/restmachine/cloudbroker/account/deleteAccounts"

@ -0,0 +1,108 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/flattens"
)
func dataSourceAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
acc, err := utilityAccountCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("dc_location", acc.DCLocation)
d.Set("resources", flattenAccResources(acc.Resources))
d.Set("ckey", acc.CKey)
d.Set("meta", flattens.FlattenMeta(acc.Meta))
d.Set("acl", flattenAccAcl(acc.Acl))
d.Set("company", acc.Company)
d.Set("companyurl", acc.CompanyUrl)
d.Set("created_by", acc.CreatedBy)
d.Set("created_time", acc.CreatedTime)
d.Set("deactivation_time", acc.DeactiovationTime)
d.Set("deleted_by", acc.DeletedBy)
d.Set("deleted_time", acc.DeletedTime)
d.Set("displayname", acc.DisplayName)
d.Set("guid", acc.GUID)
d.Set("account_id", acc.ID)
d.Set("account_name", acc.Name)
d.Set("resource_limits", flattenRgResourceLimits(acc.ResourceLimits))
d.Set("send_access_emails", acc.SendAccessEmails)
d.Set("service_account", acc.ServiceAccount)
d.Set("status", acc.Status)
d.Set("updated_time", acc.UpdatedTime)
d.Set("version", acc.Version)
d.Set("vins", acc.Vins)
return nil
}
func flattenAccAcl(acls []AccountAclRecord) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, acls := range acls {
temp := map[string]interface{}{
"explicit": acls.IsExplicit,
"guid": acls.Guid,
"right": acls.Rights,
"status": acls.Status,
"type": acls.Type,
"user_group_id": acls.UgroupID,
}
res = append(res, temp)
}
return res
}
func DataSourceAccount() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountSchemaMake(),
}
}

@ -0,0 +1,104 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenAccountAuditsList(aal AccountAuditsList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, aa := range aal {
temp := map[string]interface{}{
"call": aa.Call,
"responsetime": aa.ResponseTime,
"statuscode": aa.StatusCode,
"timestamp": aa.Timestamp,
"user": aa.User,
}
res = append(res, temp)
}
return res
}
func dataSourceAccountAuditsListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountAuditsList, err := utilityAccountAuditsListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenAccountAuditsList(accountAuditsList))
return nil
}
func dataSourceAccountAuditsListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the account",
},
"items": {
Type: schema.TypeList,
Computed: true,
Description: "Search Result",
Elem: &schema.Resource{
Schema: dataSourceAccountAuditSchemaMake(),
},
},
}
return res
}
func DataSourceAccountAuditsList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountAuditsListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountAuditsListSchemaMake(),
}
}

@ -0,0 +1,119 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenAccountComputesList(acl AccountComputesList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, acc := range acl {
temp := map[string]interface{}{
"account_id": acc.AccountId,
"account_name": acc.AccountName,
"cpus": acc.CPUs,
"created_by": acc.CreatedBy,
"created_time": acc.CreatedTime,
"deleted_by": acc.DeletedBy,
"deleted_time": acc.DeletedTime,
"compute_id": acc.ComputeId,
"compute_name": acc.ComputeName,
"ram": acc.RAM,
"registered": acc.Registered,
"rg_id": acc.RgId,
"rg_name": acc.RgName,
"status": acc.Status,
"tech_status": acc.TechStatus,
"total_disks_size": acc.TotalDisksSize,
"updated_by": acc.UpdatedBy,
"updated_time": acc.UpdatedTime,
"user_managed": acc.UserManaged,
"vins_connected": acc.VinsConnected,
}
res = append(res, temp)
}
return res
}
func dataSourceAccountComputesListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountComputesList, err := utilityAccountComputesListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenAccountComputesList(accountComputesList))
return nil
}
func dataSourceAccountComputesListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the account",
},
"items": {
Type: schema.TypeList,
Computed: true,
Description: "Search Result",
Elem: &schema.Resource{
Schema: dataSourceAccountComputeSchemaMake(),
},
},
}
return res
}
func DataSourceAccountComputesList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountComputesListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountComputesListSchemaMake(),
}
}

@ -0,0 +1,69 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func dataSourceAccountDeletedListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountDeletedList, err := utilityAccountDeletedListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenAccountList(accountDeletedList))
return nil
}
func DataSourceAccountDeletedList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountDeletedListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountListSchemaMake(),
}
}

@ -0,0 +1,104 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenAccountDisksList(adl AccountDisksList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, ad := range adl {
temp := map[string]interface{}{
"disk_id": ad.ID,
"disk_name": ad.Name,
"pool_name": ad.Pool,
"sep_id": ad.SepId,
"size_max": ad.SizeMax,
"type": ad.Type,
}
res = append(res, temp)
}
return res
}
func dataSourceAccountDisksListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountDisksList, err := utilityAccountDisksListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenAccountDisksList(accountDisksList))
return nil
}
func dataSourceAccountDisksListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the account",
},
"items": {
Type: schema.TypeList,
Computed: true,
Description: "Search Result",
Elem: &schema.Resource{
Schema: dataSourceAccountDiskSchemaMake(),
},
},
}
return res
}
func DataSourceAccountDisksList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountDisksListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountDisksListSchemaMake(),
}
}

@ -0,0 +1,120 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenAccountFlipGroupsList(afgl AccountFlipGroupsList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, afg := range afgl {
temp := map[string]interface{}{
"account_id": afg.AccountId,
"client_type": afg.ClientType,
"conn_type": afg.ConnType,
"created_by": afg.CreatedBy,
"created_time": afg.CreatedTime,
"default_gw": afg.DefaultGW,
"deleted_by": afg.DeletedBy,
"deleted_time": afg.DeletedTime,
"desc": afg.Desc,
"gid": afg.GID,
"guid": afg.GUID,
"fg_id": afg.ID,
"ip": afg.IP,
"milestones": afg.Milestones,
"fg_name": afg.Name,
"net_id": afg.NetID,
"net_type": afg.NetType,
"netmask": afg.NetMask,
"status": afg.Status,
"updated_by": afg.UpdatedBy,
"updated_time": afg.UpdatedTime,
}
res = append(res, temp)
}
return res
}
func dataSourceAccountFlipGroupsListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountFlipGroupsList, err := utilityAccountFlipGroupsListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenAccountFlipGroupsList(accountFlipGroupsList))
return nil
}
func dataSourceAccountFlipGroupsListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the account",
},
"items": {
Type: schema.TypeList,
Computed: true,
Description: "Search Result",
Elem: &schema.Resource{
Schema: dataSourceAccountFlipGroupSchemaMake(),
},
},
}
return res
}
func DataSourceAccountFlipGroupsList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountFlipGroupsListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountFlipGroupsListSchemaMake(),
}
}

@ -0,0 +1,167 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/flattens"
)
func flattenRgAcl(rgAcls []AccountAclRecord) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, rgAcl := range rgAcls {
temp := map[string]interface{}{
"explicit": rgAcl.IsExplicit,
"guid": rgAcl.Guid,
"right": rgAcl.Rights,
"status": rgAcl.Status,
"type": rgAcl.Type,
"user_group_id": rgAcl.UgroupID,
}
res = append(res, temp)
}
return res
}
func flattenAccountList(al AccountList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, acc := range al {
temp := map[string]interface{}{
"dc_location": acc.DCLocation,
"ckey": acc.CKey,
"meta": flattens.FlattenMeta(acc.Meta),
"acl": flattenRgAcl(acc.Acl),
"company": acc.Company,
"companyurl": acc.CompanyUrl,
"created_by": acc.CreatedBy,
"created_time": acc.CreatedTime,
"deactivation_time": acc.DeactiovationTime,
"deleted_by": acc.DeletedBy,
"deleted_time": acc.DeletedTime,
"displayname": acc.DisplayName,
"guid": acc.GUID,
"account_id": acc.ID,
"account_name": acc.Name,
"resource_limits": flattenRgResourceLimits(acc.ResourceLimits),
"send_access_emails": acc.SendAccessEmails,
"service_account": acc.ServiceAccount,
"status": acc.Status,
"updated_time": acc.UpdatedTime,
"version": acc.Version,
"vins": acc.Vins,
}
res = append(res, temp)
}
return res
}
func flattenRgResourceLimits(rl ResourceLimits) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"cu_c": rl.CUC,
"cu_d": rl.CUD,
"cu_i": rl.CUI,
"cu_m": rl.CUM,
"cu_np": rl.CUNP,
"gpu_units": rl.GpuUnits,
}
res = append(res, temp)
return res
}
func dataSourceAccountListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountList, err := utilityAccountListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenAccountList(accountList))
return nil
}
func dataSourceAccountListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"page": {
Type: schema.TypeInt,
Optional: true,
Description: "Page number",
},
"size": {
Type: schema.TypeInt,
Optional: true,
Description: "Page size",
},
"items": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: dataSourceAccountItemSchemaMake(),
},
},
}
return res
}
func DataSourceAccountList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountListSchemaMake(),
}
}

@ -0,0 +1,157 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenAccountRGList(argl AccountRGList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, arg := range argl {
temp := map[string]interface{}{
"computes": flattenAccRGComputes(arg.Computes),
"resources": flattenAccRGResources(arg.Resources),
"created_by": arg.CreatedBy,
"created_time": arg.CreatedTime,
"deleted_by": arg.DeletedBy,
"deleted_time": arg.DeletedTime,
"rg_id": arg.RGID,
"milestones": arg.Milestones,
"rg_name": arg.RGName,
"status": arg.Status,
"updated_by": arg.UpdatedBy,
"updated_time": arg.UpdatedTime,
"vinses": arg.Vinses,
}
res = append(res, temp)
}
return res
}
func flattenAccRGComputes(argc AccountRGComputes) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"started": argc.Started,
"stopped": argc.Stopped,
}
res = append(res, temp)
return res
}
func flattenAccRGResources(argr AccountRGResources) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"consumed": flattenAccResource(argr.Consumed),
"limits": flattenAccResource(argr.Limits),
"reserved": flattenAccResource(argr.Reserved),
}
res = append(res, temp)
return res
}
func flattenAccResources(r Resources) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"current": flattenAccResource(r.Current),
"reserved": flattenAccResource(r.Reserved),
}
res = append(res, temp)
return res
}
func flattenAccResource(r Resource) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"cpu": r.CPU,
"disksize": r.Disksize,
"extips": r.Extips,
"exttraffic": r.Exttraffic,
"gpu": r.GPU,
"ram": r.RAM,
}
res = append(res, temp)
return res
}
func dataSourceAccountRGListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountRGList, err := utilityAccountRGListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenAccountRGList(accountRGList))
return nil
}
func dataSourceAccountRGListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the account",
},
"items": {
Type: schema.TypeList,
Computed: true,
Description: "Search Result",
Elem: &schema.Resource{
Schema: dataSourceAccountRGSchemaMake(),
},
},
}
return res
}
func DataSourceAccountRGList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountRGListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountRGListSchemaMake(),
}
}

@ -0,0 +1,116 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenAccountVinsList(avl AccountVinsList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, av := range avl {
temp := map[string]interface{}{
"account_id": av.AccountId,
"account_name": av.AccountName,
"computes": av.Computes,
"created_by": av.CreatedBy,
"created_time": av.CreatedTime,
"deleted_by": av.DeletedBy,
"deleted_time": av.DeletedTime,
"external_ip": av.ExternalIP,
"vin_id": av.ID,
"vin_name": av.Name,
"network": av.Network,
"pri_vnf_dev_id": av.PriVnfDevId,
"rg_id": av.RgId,
"rg_name": av.RgName,
"status": av.Status,
"updated_by": av.UpdatedBy,
"updated_time": av.UpdatedTime,
}
res = append(res, temp)
}
return res
}
func dataSourceAccountVinsListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountVinsList, err := utilityAccountVinsListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenAccountVinsList(accountVinsList))
return nil
}
func dataSourceAccountVinsListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the account",
},
"items": {
Type: schema.TypeList,
Computed: true,
Description: "Search Result",
Elem: &schema.Resource{
Schema: dataSourceAccountVinsSchema(),
},
},
}
return res
}
func DataSourceAccountVinsList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceAccountVinsListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceAccountVinsListSchemaMake(),
}
}

@ -0,0 +1,219 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
type AccountAclRecord struct {
IsExplicit bool `json:"explicit"`
Guid string `json:"guid"`
Rights string `json:"right"`
Status string `json:"status"`
Type string `json:"type"`
UgroupID string `json:"userGroupId"`
}
type ResourceLimits struct {
CUC float64 `json:"CU_C"`
CUD float64 `json:"CU_D"`
CUI float64 `json:"CU_I"`
CUM float64 `json:"CU_M"`
CUNP float64 `json:"CU_NP"`
GpuUnits float64 `json:"gpu_units"`
}
type Account struct {
DCLocation string `json:"DCLocation"`
CKey string `jspn:"_ckey"`
Meta []interface{} `json:"_meta"`
Acl []AccountAclRecord `json:"acl"`
Company string `json:"company"`
CompanyUrl string `json:"companyurl"`
CreatedBy string `jspn:"createdBy"`
CreatedTime int `json:"createdTime"`
DeactiovationTime float64 `json:"deactivationTime"`
DeletedBy string `json:"deletedBy"`
DeletedTime int `json:"deletedTime"`
DisplayName string `json:"displayname"`
GUID int `json:"guid"`
ID int `json:"id"`
Name string `json:"name"`
ResourceLimits ResourceLimits `json:"resourceLimits"`
SendAccessEmails bool `json:"sendAccessEmails"`
ServiceAccount bool `json:"serviceAccount"`
Status string `json:"status"`
UpdatedTime int `json:"updatedTime"`
Version int `json:"version"`
Vins []int `json:"vins"`
}
type AccountList []Account
type Resource struct {
CPU int `json:"cpu"`
Disksize int `json:"disksize"`
Extips int `json:"extips"`
Exttraffic int `json:"exttraffic"`
GPU int `json:"gpu"`
RAM int `json:"ram"`
}
type Resources struct {
Current Resource `json:"Current"`
Reserved Resource `json:"Reserved"`
}
type AccountWithResources struct {
Account
Resources Resources `json:"Resources"`
}
type AccountCompute struct {
AccountId int `json:"accountId"`
AccountName string `json:"accountName"`
CPUs int `json:"cpus"`
CreatedBy string `json:"createdBy"`
CreatedTime int `json:"createdTime"`
DeletedBy string `json:"deletedBy"`
DeletedTime int `json:"deletedTime"`
ComputeId int `json:"id"`
ComputeName string `json:"name"`
RAM int `json:"ram"`
Registered bool `json:"registered"`
RgId int `json:"rgId"`
RgName string `json:"rgName"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
TotalDisksSize int `json:"totalDisksSize"`
UpdatedBy string `json:"updatedBy"`
UpdatedTime int `json:"updatedTime"`
UserManaged bool `json:"userManaged"`
VinsConnected int `json:"vinsConnected"`
}
type AccountComputesList []AccountCompute
type AccountDisk struct {
ID int `json:"id"`
Name string `json:"name"`
Pool string `json:"pool"`
SepId int `json:"sepId"`
SizeMax int `json:"sizeMax"`
Type string `json:"type"`
}
type AccountDisksList []AccountDisk
type AccountVin struct {
AccountId int `json:"accountId"`
AccountName string `json:"accountName"`
Computes int `json:"computes"`
CreatedBy string `json:"createdBy"`
CreatedTime int `json:"createdTime"`
DeletedBy string `json:"deletedBy"`
DeletedTime int `json:"deletedTime"`
ExternalIP string `json:"externalIP"`
ID int `json:"id"`
Name string `json:"name"`
Network string `json:"network"`
PriVnfDevId int `json:"priVnfDevId"`
RgId int `json:"rgId"`
RgName string `json:"rgName"`
Status string `json:"status"`
UpdatedBy string `json:"updatedBy"`
UpdatedTime int `json:"updatedTime"`
}
type AccountVinsList []AccountVin
type AccountAudit struct {
Call string `json:"call"`
ResponseTime float64 `json:"responsetime"`
StatusCode int `json:"statuscode"`
Timestamp float64 `json:"timestamp"`
User string `json:"user"`
}
type AccountAuditsList []AccountAudit
type AccountRGComputes struct {
Started int `json:"Started"`
Stopped int `json:"Stopped"`
}
type AccountRGResources struct {
Consumed Resource `json:"Consumed"`
Limits Resource `json:"Limits"`
Reserved Resource `json:"Reserved"`
}
type AccountRG struct {
Computes AccountRGComputes `json:"Computes"`
Resources AccountRGResources `json:"Resources"`
CreatedBy string `json:"createdBy"`
CreatedTime int `json:"createdTime"`
DeletedBy string `json:"deletedBy"`
DeletedTime int `json:"deletedTime"`
RGID int `json:"id"`
Milestones int `json:"milestones"`
RGName string `json:"name"`
Status string `json:"status"`
UpdatedBy string `json:"updatedBy"`
UpdatedTime int `json:"updatedTime"`
Vinses int `json:"vinses"`
}
type AccountRGList []AccountRG
type AccountFlipGroup struct {
AccountId int `json:"accountId"`
ClientType string `json:"clientType"`
ConnType string `json:"connType"`
CreatedBy string `json:"createdBy"`
CreatedTime int `json:"createdTime"`
DefaultGW string `json:"defaultGW"`
DeletedBy string `json:"deletedBy"`
DeletedTime int `json:"deletedTime"`
Desc string `json:"desc"`
GID int `json:"gid"`
GUID int `json:"guid"`
ID int `json:"id"`
IP string `json:"ip"`
Milestones int `json:"milestones"`
Name string `json:"name"`
NetID int `json:"netId"`
NetType string `json:"netType"`
NetMask int `json:"netmask"`
Status string `json:"status"`
UpdatedBy string `json:"updatedBy"`
UpdatedTime int `json:"updatedTime"`
}
type AccountFlipGroupsList []AccountFlipGroup

@ -0,0 +1,485 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
"github.com/rudecs/terraform-provider-decort/internal/flattens"
log "github.com/sirupsen/logrus"
)
func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceAccountCreate")
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("name", d.Get("account_name").(string))
urlValues.Add("username", d.Get("username").(string))
if emailaddress, ok := d.GetOk("emailaddress"); ok {
urlValues.Add("emailaddress", emailaddress.(string))
}
if sendAccessEmails, ok := d.GetOk("send_access_emails"); ok {
urlValues.Add("sendAccessEmails", strconv.FormatBool(sendAccessEmails.(bool)))
}
if resLimits, ok := d.GetOk("resource_limits"); ok {
resLimit := resLimits.([]interface{})[0]
resLimitConv := resLimit.(map[string]interface{})
if resLimitConv["cu_m"] != nil {
maxMemCap := int(resLimitConv["cu_m"].(float64))
if maxMemCap == 0 {
urlValues.Add("maxMemoryCapacity", strconv.Itoa(-1))
} else {
urlValues.Add("maxMemoryCapacity", strconv.Itoa(maxMemCap))
}
}
if resLimitConv["cu_d"] != nil {
maxDiskCap := int(resLimitConv["cu_d"].(float64))
if maxDiskCap == 0 {
urlValues.Add("maxVDiskCapacity", strconv.Itoa(-1))
} else {
urlValues.Add("maxVDiskCapacity", strconv.Itoa(maxDiskCap))
}
}
if resLimitConv["cu_c"] != nil {
maxCPUCap := int(resLimitConv["cu_c"].(float64))
if maxCPUCap == 0 {
urlValues.Add("maxCPUCapacity", strconv.Itoa(-1))
} else {
urlValues.Add("maxCPUCapacity", strconv.Itoa(maxCPUCap))
}
}
if resLimitConv["cu_i"] != nil {
maxNumPublicIP := int(resLimitConv["cu_i"].(float64))
if maxNumPublicIP == 0 {
urlValues.Add("maxNumPublicIP", strconv.Itoa(-1))
} else {
urlValues.Add("maxNumPublicIP", strconv.Itoa(maxNumPublicIP))
}
}
if resLimitConv["cu_np"] != nil {
maxNP := int(resLimitConv["cu_np"].(float64))
if maxNP == 0 {
urlValues.Add("maxNetworkPeerTransfer", strconv.Itoa(-1))
} else {
urlValues.Add("maxNetworkPeerTransfer", strconv.Itoa(maxNP))
}
}
if resLimitConv["gpu_units"] != nil {
gpuUnits := int(resLimitConv["gpu_units"].(float64))
if gpuUnits == 0 {
urlValues.Add("gpu_units", strconv.Itoa(-1))
} else {
urlValues.Add("gpu_units", strconv.Itoa(gpuUnits))
}
}
}
accountId, err := c.DecortAPICall(ctx, "POST", accountCreateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId(accountId)
d.Set("account_id", accountId)
diagnostics := resourceAccountRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
if enable, ok := d.GetOk("enable"); ok {
api := accountDisableAPI
enable := enable.(bool)
if enable {
api = accountEnableAPI
}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
_, err := c.DecortAPICall(ctx, "POST", api, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
if users, ok := d.GetOk("users"); ok {
addedUsers := users.([]interface{})
if len(addedUsers) > 0 {
for _, user := range addedUsers {
userConv := user.(map[string]interface{})
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
urlValues.Add("userId", userConv["user_id"].(string))
urlValues.Add("accesstype", strings.ToUpper(userConv["access_type"].(string)))
_, err := c.DecortAPICall(ctx, "POST", accountAddUserAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
}
}
return nil
}
func resourceAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceAccountRead")
acc, err := utilityAccountCheckPresence(ctx, d, m)
if acc == nil {
d.SetId("")
return diag.FromErr(err)
}
d.Set("dc_location", acc.DCLocation)
d.Set("resources", flattenAccResources(acc.Resources))
d.Set("ckey", acc.CKey)
d.Set("meta", flattens.FlattenMeta(acc.Meta))
d.Set("acl", flattenAccAcl(acc.Acl))
d.Set("company", acc.Company)
d.Set("companyurl", acc.CompanyUrl)
d.Set("created_by", acc.CreatedBy)
d.Set("created_time", acc.CreatedTime)
d.Set("deactivation_time", acc.DeactiovationTime)
d.Set("deleted_by", acc.DeletedBy)
d.Set("deleted_time", acc.DeletedTime)
d.Set("displayname", acc.DisplayName)
d.Set("guid", acc.GUID)
d.Set("account_id", acc.ID)
d.Set("account_name", acc.Name)
d.Set("resource_limits", flattenRgResourceLimits(acc.ResourceLimits))
d.Set("send_access_emails", acc.SendAccessEmails)
d.Set("service_account", acc.ServiceAccount)
d.Set("status", acc.Status)
d.Set("updated_time", acc.UpdatedTime)
d.Set("version", acc.Version)
d.Set("vins", acc.Vins)
return nil
}
func resourceAccountDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceAccountDelete")
account, err := utilityAccountCheckPresence(ctx, d, m)
if account == nil {
if err != nil {
return diag.FromErr(err)
}
return nil
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
urlValues.Add("permanently", strconv.FormatBool(d.Get("permanently").(bool)))
_, err = c.DecortAPICall(ctx, "POST", accountDeleteAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId("")
return nil
}
func resourceAccountExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
log.Debugf("resourceAccountExists")
account, err := utilityAccountCheckPresence(ctx, d, m)
if account == nil {
if err != nil {
return false, err
}
return false, nil
}
return true, nil
}
func resourceAccountEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceAccountEdit")
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
if d.HasChange("enable") {
api := accountDisableAPI
enable := d.Get("enable").(bool)
if enable {
api = accountEnableAPI
}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
_, err := c.DecortAPICall(ctx, "POST", api, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
if d.HasChange("account_name") {
urlValues.Add("name", d.Get("account_name").(string))
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
_, err := c.DecortAPICall(ctx, "POST", accountUpdateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
if d.HasChange("resource_limits") {
resLimit := d.Get("resource_limits").([]interface{})[0]
resLimitConv := resLimit.(map[string]interface{})
if resLimitConv["cu_m"] != nil {
maxMemCap := int(resLimitConv["cu_m"].(float64))
if maxMemCap == 0 {
urlValues.Add("maxMemoryCapacity", strconv.Itoa(-1))
} else {
urlValues.Add("maxMemoryCapacity", strconv.Itoa(maxMemCap))
}
}
if resLimitConv["cu_d"] != nil {
maxDiskCap := int(resLimitConv["cu_d"].(float64))
if maxDiskCap == 0 {
urlValues.Add("maxVDiskCapacity", strconv.Itoa(-1))
} else {
urlValues.Add("maxVDiskCapacity", strconv.Itoa(maxDiskCap))
}
}
if resLimitConv["cu_c"] != nil {
maxCPUCap := int(resLimitConv["cu_c"].(float64))
if maxCPUCap == 0 {
urlValues.Add("maxCPUCapacity", strconv.Itoa(-1))
} else {
urlValues.Add("maxCPUCapacity", strconv.Itoa(maxCPUCap))
}
}
if resLimitConv["cu_i"] != nil {
maxNumPublicIP := int(resLimitConv["cu_i"].(float64))
if maxNumPublicIP == 0 {
urlValues.Add("maxNumPublicIP", strconv.Itoa(-1))
} else {
urlValues.Add("maxNumPublicIP", strconv.Itoa(maxNumPublicIP))
}
}
if resLimitConv["cu_np"] != nil {
maxNP := int(resLimitConv["cu_np"].(float64))
if maxNP == 0 {
urlValues.Add("maxNetworkPeerTransfer", strconv.Itoa(-1))
} else {
urlValues.Add("maxNetworkPeerTransfer", strconv.Itoa(maxNP))
}
}
if resLimitConv["gpu_units"] != nil {
gpuUnits := int(resLimitConv["gpu_units"].(float64))
if gpuUnits == 0 {
urlValues.Add("gpu_units", strconv.Itoa(-1))
} else {
urlValues.Add("gpu_units", strconv.Itoa(gpuUnits))
}
}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
_, err := c.DecortAPICall(ctx, "POST", accountUpdateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
if d.HasChange("send_access_emails") {
urlValues.Add("sendAccessEmails", strconv.FormatBool(d.Get("send_access_emails").(bool)))
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
_, err := c.DecortAPICall(ctx, "POST", accountUpdateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
if d.HasChange("restore") {
restore := d.Get("restore").(bool)
if restore {
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
_, err := c.DecortAPICall(ctx, "POST", accountRestoreAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
}
if d.HasChange("users") {
deletedUsers := make([]interface{}, 0)
addedUsers := make([]interface{}, 0)
updatedUsers := make([]interface{}, 0)
old, new := d.GetChange("users")
oldConv := old.([]interface{})
newConv := new.([]interface{})
for _, el := range oldConv {
if !isContainsUser(newConv, el) {
deletedUsers = append(deletedUsers, el)
}
}
for _, el := range newConv {
if !isContainsUser(oldConv, el) {
addedUsers = append(addedUsers, el)
} else {
if isChangedUser(oldConv, el) {
updatedUsers = append(updatedUsers, el)
}
}
}
if len(deletedUsers) > 0 {
for _, user := range deletedUsers {
userConv := user.(map[string]interface{})
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
urlValues.Add("userId", userConv["user_id"].(string))
urlValues.Add("recursivedelete", strconv.FormatBool(userConv["recursive_delete"].(bool)))
_, err := c.DecortAPICall(ctx, "POST", accountDeleteUserAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
}
if len(addedUsers) > 0 {
for _, user := range addedUsers {
userConv := user.(map[string]interface{})
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
urlValues.Add("userId", userConv["user_id"].(string))
urlValues.Add("accesstype", strings.ToUpper(userConv["access_type"].(string)))
_, err := c.DecortAPICall(ctx, "POST", accountAddUserAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
}
if len(updatedUsers) > 0 {
for _, user := range updatedUsers {
userConv := user.(map[string]interface{})
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
urlValues.Add("userId", userConv["user_id"].(string))
urlValues.Add("accesstype", strings.ToUpper(userConv["access_type"].(string)))
_, err := c.DecortAPICall(ctx, "POST", accountUpdateUserAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
}
}
return nil
}
func isContainsUser(els []interface{}, el interface{}) bool {
for _, elOld := range els {
elOldConv := elOld.(map[string]interface{})
elConv := el.(map[string]interface{})
if elOldConv["user_id"].(string) == elConv["user_id"].(string) {
return true
}
}
return false
}
func isChangedUser(els []interface{}, el interface{}) bool {
for _, elOld := range els {
elOldConv := elOld.(map[string]interface{})
elConv := el.(map[string]interface{})
if elOldConv["user_id"].(string) == elConv["user_id"].(string) &&
(!strings.EqualFold(elOldConv["access_type"].(string), elConv["access_type"].(string)) ||
elOldConv["recursive_delete"].(bool) != elConv["recursive_delete"].(bool)) {
return true
}
}
return false
}
func ResourceAccount() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceAccountCreate,
ReadContext: resourceAccountRead,
UpdateContext: resourceAccountEdit,
DeleteContext: resourceAccountDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout60s,
Read: &constants.Timeout30s,
Update: &constants.Timeout60s,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: resourceAccountSchemaMake(),
}
}

@ -0,0 +1,69 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*AccountWithResources, error) {
account := &AccountWithResources{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
if (strconv.Itoa(d.Get("account_id").(int))) != "0" {
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
} else {
urlValues.Add("accountId", d.Id())
}
log.Debugf("utilityAccountCheckPresence: load account")
accountRaw, err := c.DecortAPICall(ctx, "POST", accountGetAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountRaw), &account)
if err != nil {
return nil, err
}
return account, nil
}

@ -0,0 +1,65 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountAuditsListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (AccountAuditsList, error) {
accountAuditsList := AccountAuditsList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
log.Debugf("utilityAccountAuditsListCheckPresence: load account list")
accountAuditsListRaw, err := c.DecortAPICall(ctx, "POST", accountAuditsAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountAuditsListRaw), &accountAuditsList)
if err != nil {
return nil, err
}
return accountAuditsList, nil
}

@ -0,0 +1,65 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountComputesListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (AccountComputesList, error) {
accountComputesList := AccountComputesList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
log.Debugf("utilityAccountComputesListCheckPresence: load account list")
accountComputesListRaw, err := c.DecortAPICall(ctx, "POST", accountListComputesAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountComputesListRaw), &accountComputesList)
if err != nil {
return nil, err
}
return accountComputesList, nil
}

@ -0,0 +1,70 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountDeletedListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (AccountList, error) {
accountDeletedList := AccountList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
if page, ok := d.GetOk("page"); ok {
urlValues.Add("page", strconv.Itoa(page.(int)))
}
if size, ok := d.GetOk("size"); ok {
urlValues.Add("size", strconv.Itoa(size.(int)))
}
log.Debugf("utilityAccountDeletedListCheckPresence: load")
accountDeletedListRaw, err := c.DecortAPICall(ctx, "POST", accountListDeletedAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountDeletedListRaw), &accountDeletedList)
if err != nil {
return nil, err
}
return accountDeletedList, nil
}

@ -0,0 +1,65 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountDisksListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (AccountDisksList, error) {
accountDisksList := AccountDisksList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
log.Debugf("utilityAccountDisksListCheckPresence: load account list")
accountDisksListRaw, err := c.DecortAPICall(ctx, "POST", accountListDisksAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountDisksListRaw), &accountDisksList)
if err != nil {
return nil, err
}
return accountDisksList, nil
}

@ -0,0 +1,65 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountFlipGroupsListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (AccountFlipGroupsList, error) {
accountFlipGroupsList := AccountFlipGroupsList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
log.Debugf("utilityAccountFlipGroupsListCheckPresence")
accountFlipGroupsListRaw, err := c.DecortAPICall(ctx, "POST", accountListFlipGroupsAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountFlipGroupsListRaw), &accountFlipGroupsList)
if err != nil {
return nil, err
}
return accountFlipGroupsList, nil
}

@ -0,0 +1,70 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (AccountList, error) {
accountList := AccountList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
if page, ok := d.GetOk("page"); ok {
urlValues.Add("page", strconv.Itoa(page.(int)))
}
if size, ok := d.GetOk("size"); ok {
urlValues.Add("size", strconv.Itoa(size.(int)))
}
log.Debugf("utilityAccountListCheckPresence: load account list")
accountListRaw, err := c.DecortAPICall(ctx, "POST", accountListAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountListRaw), &accountList)
if err != nil {
return nil, err
}
return accountList, nil
}

@ -0,0 +1,65 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountRGListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (AccountRGList, error) {
accountRGList := AccountRGList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
log.Debugf("utilityAccountRGListCheckPresence: load account list")
accountRGListRaw, err := c.DecortAPICall(ctx, "POST", accountListRGAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountRGListRaw), &accountRGList)
if err != nil {
return nil, err
}
return accountRGList, nil
}

@ -0,0 +1,65 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package account
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityAccountVinsListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (AccountVinsList, error) {
accountVinsList := AccountVinsList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int)))
log.Debugf("utilityAccountVinsListCheckPresence: load account list")
accountVinsListRaw, err := c.DecortAPICall(ctx, "POST", accountListVinsAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(accountVinsListRaw), &accountVinsList)
if err != nil {
return nil, err
}
return accountVinsList, nil
}

@ -0,0 +1,41 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package disks
const disksCreateAPI = "/restmachine/cloudbroker/disks/create"
const disksGetAPI = "/restmachine/cloudbroker/disks/get"
const disksListAPI = "/restmachine/cloudbroker/disks/list"
const disksResizeAPI = "/restmachine/cloudbroker/disks/resize2"
const disksRenameAPI = "/restmachine/cloudbroker/disks/rename"
const disksDeleteAPI = "/restmachine/cloudbroker/disks/delete"
const disksIOLimitAPI = "/restmachine/cloudbroker/disks/limitIO"
const disksRestoreAPI = "/restmachine/cloudbroker/disks/restore"

@ -0,0 +1,389 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package disks
import (
"context"
"encoding/json"
// "net/url"
"github.com/google/uuid"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func dataSourceDiskRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
disk, err := utilityDiskCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
diskAcl, _ := json.Marshal(disk.Acl)
d.Set("account_id", disk.AccountID)
d.Set("account_name", disk.AccountName)
d.Set("acl", string(diskAcl))
d.Set("boot_partition", disk.BootPartition)
d.Set("compute_id", disk.ComputeID)
d.Set("compute_name", disk.ComputeName)
d.Set("created_time", disk.CreatedTime)
d.Set("deleted_time", disk.DeletedTime)
d.Set("desc", disk.Desc)
d.Set("destruction_time", disk.DestructionTime)
d.Set("devicename", disk.DeviceName)
d.Set("disk_path", disk.DiskPath)
d.Set("gid", disk.GridID)
d.Set("guid", disk.GUID)
d.Set("disk_id", disk.ID)
d.Set("image_id", disk.ImageID)
d.Set("images", disk.Images)
d.Set("iotune", flattenIOTune(disk.IOTune))
d.Set("iqn", disk.IQN)
d.Set("login", disk.Login)
d.Set("milestones", disk.Milestones)
d.Set("disk_name", disk.Name)
d.Set("order", disk.Order)
d.Set("params", disk.Params)
d.Set("parent_id", disk.ParentId)
d.Set("passwd", disk.Passwd)
d.Set("pci_slot", disk.PciSlot)
d.Set("pool", disk.Pool)
d.Set("purge_attempts", disk.PurgeAttempts)
d.Set("purge_time", disk.PurgeTime)
d.Set("reality_device_number", disk.RealityDeviceNumber)
d.Set("reference_id", disk.ReferenceId)
d.Set("res_id", disk.ResID)
d.Set("res_name", disk.ResName)
d.Set("role", disk.Role)
d.Set("sep_id", disk.SepID)
d.Set("sep_type", disk.SepType)
d.Set("size_max", disk.SizeMax)
d.Set("size_used", disk.SizeUsed)
d.Set("snapshots", flattendDiskSnapshotList(disk.Snapshots))
d.Set("status", disk.Status)
d.Set("tech_status", disk.TechStatus)
d.Set("type", disk.Type)
d.Set("vmid", disk.VMID)
return nil
}
func dataSourceDiskSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"disk_id": {
Type: schema.TypeInt,
Required: true,
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"acl": {
Type: schema.TypeString,
Computed: true,
},
"boot_partition": {
Type: schema.TypeInt,
Computed: true,
},
"compute_id": {
Type: schema.TypeInt,
Computed: true,
},
"compute_name": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Computed: true,
},
"destruction_time": {
Type: schema.TypeInt,
Computed: true,
},
"devicename": {
Type: schema.TypeString,
Computed: true,
},
"disk_path": {
Type: schema.TypeString,
Computed: true,
},
"gid": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"image_id": {
Type: schema.TypeInt,
Computed: true,
},
"images": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"iotune": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"read_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"read_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"read_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"read_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"size_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"total_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"write_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"write_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"write_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"write_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"iqn": {
Type: schema.TypeString,
Computed: true,
},
"login": {
Type: schema.TypeString,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"disk_name": {
Type: schema.TypeString,
Computed: true,
},
"order": {
Type: schema.TypeInt,
Computed: true,
},
"params": {
Type: schema.TypeString,
Computed: true,
},
"parent_id": {
Type: schema.TypeInt,
Computed: true,
},
"passwd": {
Type: schema.TypeString,
Computed: true,
},
"pci_slot": {
Type: schema.TypeInt,
Computed: true,
},
"pool": {
Type: schema.TypeString,
Computed: true,
},
"purge_attempts": {
Type: schema.TypeInt,
Computed: true,
},
"purge_time": {
Type: schema.TypeInt,
Computed: true,
},
"reality_device_number": {
Type: schema.TypeInt,
Computed: true,
},
"reference_id": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"res_name": {
Type: schema.TypeString,
Computed: true,
},
"role": {
Type: schema.TypeString,
Computed: true,
},
"sep_id": {
Type: schema.TypeInt,
Computed: true,
},
"sep_type": {
Type: schema.TypeString,
Computed: true,
},
"size_max": {
Type: schema.TypeInt,
Computed: true,
},
"size_used": {
Type: schema.TypeInt,
Computed: true,
},
"snapshots": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Computed: true,
},
"label": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_guid": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_time": {
Type: schema.TypeInt,
Computed: true,
},
"timestamp": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"vmid": {
Type: schema.TypeInt,
Computed: true,
},
}
return rets
}
func DataSourceDisk() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceDiskRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceDiskSchemaMake(),
}
}

@ -0,0 +1,473 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package disks
import (
"context"
"encoding/json"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenIOTune(iot IOTune) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"read_bytes_sec": iot.ReadBytesSec,
"read_bytes_sec_max": iot.ReadBytesSecMax,
"read_iops_sec": iot.ReadIopsSec,
"read_iops_sec_max": iot.ReadIopsSecMax,
"size_iops_sec": iot.SizeIopsSec,
"total_bytes_sec": iot.TotalBytesSec,
"total_bytes_sec_max": iot.TotalBytesSecMax,
"total_iops_sec": iot.TotalIopsSec,
"total_iops_sec_max": iot.TotalIopsSecMax,
"write_bytes_sec": iot.WriteBytesSec,
"write_bytes_sec_max": iot.WriteBytesSecMax,
"write_iops_sec": iot.WriteIopsSec,
"write_iops_sec_max": iot.WriteIopsSecMax,
}
res = append(res, temp)
return res
}
func flattenDiskList(dl DisksList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, disk := range dl {
diskAcl, _ := json.Marshal(disk.Acl)
temp := map[string]interface{}{
"account_id": disk.AccountID,
"account_name": disk.AccountName,
"acl": string(diskAcl),
"boot_partition": disk.BootPartition,
"compute_id": disk.ComputeID,
"compute_name": disk.ComputeName,
"created_time": disk.CreatedTime,
"deleted_time": disk.DeletedTime,
"desc": disk.Desc,
"destruction_time": disk.DestructionTime,
"devicename": disk.DeviceName,
"disk_path": disk.DiskPath,
"gid": disk.GridID,
"guid": disk.GUID,
"disk_id": disk.ID,
"image_id": disk.ImageID,
"images": disk.Images,
"iotune": flattenIOTune(disk.IOTune),
"iqn": disk.IQN,
"login": disk.Login,
"machine_id": disk.MachineId,
"machine_name": disk.MachineName,
"milestones": disk.Milestones,
"disk_name": disk.Name,
"order": disk.Order,
"params": disk.Params,
"parent_id": disk.ParentId,
"passwd": disk.Passwd,
"pci_slot": disk.PciSlot,
"pool": disk.Pool,
"purge_attempts": disk.PurgeAttempts,
"purge_time": disk.PurgeTime,
"reality_device_number": disk.RealityDeviceNumber,
"reference_id": disk.ReferenceId,
"res_id": disk.ResID,
"res_name": disk.ResName,
"role": disk.Role,
"sep_id": disk.SepID,
"sep_type": disk.SepType,
"size_max": disk.SizeMax,
"size_used": disk.SizeUsed,
"snapshots": flattendDiskSnapshotList(disk.Snapshots),
"status": disk.Status,
"tech_status": disk.TechStatus,
"type": disk.Type,
"vmid": disk.VMID,
}
res = append(res, temp)
}
return res
}
func flattendDiskSnapshotList(sl SnapshotList) []interface{} {
res := make([]interface{}, 0)
for _, snapshot := range sl {
temp := map[string]interface{}{
"guid": snapshot.Guid,
"label": snapshot.Label,
"res_id": snapshot.ResId,
"snap_set_guid": snapshot.SnapSetGuid,
"snap_set_time": snapshot.SnapSetTime,
"timestamp": snapshot.TimeStamp,
}
res = append(res, temp)
}
return res
}
func dataSourceDiskListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
diskList, err := utilityDiskListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenDiskList(diskList))
return nil
}
func dataSourceDiskListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Optional: true,
Description: "ID of the account the disks belong to",
},
"type": {
Type: schema.TypeString,
Optional: true,
Description: "type of the disks",
},
"page": {
Type: schema.TypeInt,
Optional: true,
Description: "Page number",
},
"size": {
Type: schema.TypeInt,
Optional: true,
Description: "Page size",
},
"items": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"acl": {
Type: schema.TypeString,
Computed: true,
},
"boot_partition": {
Type: schema.TypeInt,
Computed: true,
},
"compute_id": {
Type: schema.TypeInt,
Computed: true,
},
"compute_name": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Computed: true,
},
"destruction_time": {
Type: schema.TypeInt,
Computed: true,
},
"devicename": {
Type: schema.TypeString,
Computed: true,
},
"disk_path": {
Type: schema.TypeString,
Computed: true,
},
"gid": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"disk_id": {
Type: schema.TypeInt,
Computed: true,
},
"image_id": {
Type: schema.TypeInt,
Computed: true,
},
"images": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"iotune": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"read_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"read_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"read_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"read_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"size_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"total_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"total_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"write_bytes_sec": {
Type: schema.TypeInt,
Computed: true,
},
"write_bytes_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
"write_iops_sec": {
Type: schema.TypeInt,
Computed: true,
},
"write_iops_sec_max": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"iqn": {
Type: schema.TypeString,
Computed: true,
},
"login": {
Type: schema.TypeString,
Computed: true,
},
"machine_id": {
Type: schema.TypeInt,
Computed: true,
},
"machine_name": {
Type: schema.TypeString,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"disk_name": {
Type: schema.TypeString,
Computed: true,
},
"order": {
Type: schema.TypeInt,
Computed: true,
},
"params": {
Type: schema.TypeString,
Computed: true,
},
"parent_id": {
Type: schema.TypeInt,
Computed: true,
},
"passwd": {
Type: schema.TypeString,
Computed: true,
},
"pci_slot": {
Type: schema.TypeInt,
Computed: true,
},
"pool": {
Type: schema.TypeString,
Computed: true,
},
"purge_attempts": {
Type: schema.TypeInt,
Computed: true,
},
"purge_time": {
Type: schema.TypeInt,
Computed: true,
},
"reality_device_number": {
Type: schema.TypeInt,
Computed: true,
},
"reference_id": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"res_name": {
Type: schema.TypeString,
Computed: true,
},
"role": {
Type: schema.TypeString,
Computed: true,
},
"sep_id": {
Type: schema.TypeInt,
Computed: true,
},
"sep_type": {
Type: schema.TypeString,
Computed: true,
},
"size_max": {
Type: schema.TypeInt,
Computed: true,
},
"size_used": {
Type: schema.TypeInt,
Computed: true,
},
"snapshots": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Computed: true,
},
"label": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_guid": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_time": {
Type: schema.TypeInt,
Computed: true,
},
"timestamp": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"vmid": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
}
return res
}
func DataSourceDiskList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceDiskListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceDiskListSchemaMake(),
}
}

@ -0,0 +1,111 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package disks
type Disk struct {
Acl map[string]interface{} `json:"acl"`
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
BootPartition int `json:"bootPartition"`
CreatedTime uint64 `json:"creationTime"`
ComputeID int `json:"computeId"`
ComputeName string `json:"computeName"`
DeletedTime uint64 `json:"deletionTime"`
DeviceName string `json:"devicename"`
Desc string `json:"desc"`
DestructionTime uint64 `json:"destructionTime"`
DiskPath string `json:"diskPath"`
GridID int `json:"gid"`
GUID int `json:"guid"`
ID uint `json:"id"`
ImageID int `json:"imageId"`
Images []int `json:"images"`
IOTune IOTune `json:"iotune"`
IQN string `json:"iqn"`
Login string `json:"login"`
Name string `json:"name"`
MachineId int `json:"machineId"`
MachineName string `json:"machineName"`
Milestones uint64 `json:"milestones"`
Order int `json:"order"`
Params string `json:"params"`
Passwd string `json:"passwd"`
ParentId int `json:"parentId"`
PciSlot int `json:"pciSlot"`
Pool string `json:"pool"`
PurgeTime uint64 `json:"purgeTime"`
PurgeAttempts uint64 `json:"purgeAttempts"`
RealityDeviceNumber int `json:"realityDeviceNumber"`
ReferenceId string `json:"referenceId"`
ResID string `json:"resId"`
ResName string `json:"resName"`
Role string `json:"role"`
SepType string `json:"sepType"`
SepID int `json:"sepId"` // NOTE: absent from compute/get output
SizeMax int `json:"sizeMax"`
SizeUsed int `json:"sizeUsed"` // sum over all snapshots of this disk to report total consumed space
Snapshots []Snapshot `json:"snapshots"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
Type string `json:"type"`
UpdateBy uint64 `json:"updateBy"`
VMID int `json:"vmid"`
}
type Snapshot struct {
Guid string `json:"guid"`
Label string `json:"label"`
ResId string `json:"resId"`
SnapSetGuid string `json:"snapSetGuid"`
SnapSetTime uint64 `json:"snapSetTime"`
TimeStamp uint64 `json:"timestamp"`
}
type SnapshotList []Snapshot
type DisksList []Disk
type IOTune struct {
ReadBytesSec int `json:"read_bytes_sec"`
ReadBytesSecMax int `json:"read_bytes_sec_max"`
ReadIopsSec int `json:"read_iops_sec"`
ReadIopsSecMax int `json:"read_iops_sec_max"`
SizeIopsSec int `json:"size_iops_sec"`
TotalBytesSec int `json:"total_bytes_sec"`
TotalBytesSecMax int `json:"total_bytes_sec_max"`
TotalIopsSec int `json:"total_iops_sec"`
TotalIopsSecMax int `json:"total_iops_sec_max"`
WriteBytesSec int `json:"write_bytes_sec"`
WriteBytesSecMax int `json:"write_bytes_sec_max"`
WriteIopsSec int `json:"write_iops_sec"`
WriteIopsSecMax int `json:"write_iops_sec_max"`
}

@ -0,0 +1,638 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package disks
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"strings"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func resourceDiskCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("accountId", fmt.Sprintf("%d", d.Get("account_id").(int)))
urlValues.Add("gid", fmt.Sprintf("%d", d.Get("gid").(int)))
urlValues.Add("name", d.Get("disk_name").(string))
urlValues.Add("size", fmt.Sprintf("%d", d.Get("size_max").(int)))
if typeRaw, ok := d.GetOk("type"); ok {
urlValues.Add("type", strings.ToUpper(typeRaw.(string)))
} else {
urlValues.Add("type", "D")
}
if sepId, ok := d.GetOk("sep_id"); ok {
urlValues.Add("sep_id", strconv.Itoa(sepId.(int)))
}
if poolName, ok := d.GetOk("pool"); ok {
urlValues.Add("pool", poolName.(string))
}
argVal, argSet := d.GetOk("desc")
if argSet {
urlValues.Add("description", argVal.(string))
}
diskId, err := c.DecortAPICall(ctx, "POST", disksCreateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
d.SetId(diskId) // update ID of the resource to tell Terraform that the disk resource exists
if iotuneRaw, ok := d.GetOk("iotune"); ok {
iot := iotuneRaw.([]interface{})[0]
iotune := iot.(map[string]interface{})
urlValues.Add("diskId", diskId)
urlValues.Add("iops", strconv.Itoa(iotune["total_iops_sec"].(int)))
urlValues.Add("read_bytes_sec", strconv.Itoa(iotune["read_bytes_sec"].(int)))
urlValues.Add("read_bytes_sec_max", strconv.Itoa(iotune["read_bytes_sec_max"].(int)))
urlValues.Add("read_iops_sec", strconv.Itoa(iotune["read_iops_sec"].(int)))
urlValues.Add("read_iops_sec_max", strconv.Itoa(iotune["read_iops_sec_max"].(int)))
urlValues.Add("size_iops_sec", strconv.Itoa(iotune["size_iops_sec"].(int)))
urlValues.Add("total_bytes_sec", strconv.Itoa(iotune["total_bytes_sec"].(int)))
urlValues.Add("total_bytes_sec_max", strconv.Itoa(iotune["total_bytes_sec_max"].(int)))
urlValues.Add("total_iops_sec_max", strconv.Itoa(iotune["total_iops_sec_max"].(int)))
urlValues.Add("write_bytes_sec", strconv.Itoa(iotune["write_bytes_sec"].(int)))
urlValues.Add("write_bytes_sec_max", strconv.Itoa(iotune["write_bytes_sec_max"].(int)))
urlValues.Add("write_iops_sec", strconv.Itoa(iotune["write_iops_sec"].(int)))
urlValues.Add("write_iops_sec_max", strconv.Itoa(iotune["write_iops_sec_max"].(int)))
_, err := c.DecortAPICall(ctx, "POST", disksIOLimitAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
dgn := resourceDiskRead(ctx, d, m)
if dgn != nil {
return dgn
}
return nil
}
func resourceDiskRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
disk, err := utilityDiskCheckPresence(ctx, d, m)
if disk == nil {
d.SetId("")
if err != nil {
return diag.FromErr(err)
}
return nil
}
diskAcl, _ := json.Marshal(disk.Acl)
d.Set("account_id", disk.AccountID)
d.Set("account_name", disk.AccountName)
d.Set("acl", string(diskAcl))
d.Set("boot_partition", disk.BootPartition)
d.Set("compute_id", disk.ComputeID)
d.Set("compute_name", disk.ComputeName)
d.Set("created_time", disk.CreatedTime)
d.Set("deleted_time", disk.DeletedTime)
d.Set("desc", disk.Desc)
d.Set("destruction_time", disk.DestructionTime)
d.Set("devicename", disk.DeviceName)
d.Set("disk_path", disk.DiskPath)
d.Set("gid", disk.GridID)
d.Set("guid", disk.GUID)
d.Set("disk_id", disk.ID)
d.Set("image_id", disk.ImageID)
d.Set("images", disk.Images)
d.Set("iotune", flattenIOTune(disk.IOTune))
d.Set("iqn", disk.IQN)
d.Set("login", disk.Login)
d.Set("milestones", disk.Milestones)
d.Set("disk_name", disk.Name)
d.Set("order", disk.Order)
d.Set("params", disk.Params)
d.Set("parent_id", disk.ParentId)
d.Set("passwd", disk.Passwd)
d.Set("pci_slot", disk.PciSlot)
d.Set("pool", disk.Pool)
d.Set("purge_attempts", disk.PurgeAttempts)
d.Set("purge_time", disk.PurgeTime)
d.Set("reality_device_number", disk.RealityDeviceNumber)
d.Set("reference_id", disk.ReferenceId)
d.Set("res_id", disk.ResID)
d.Set("res_name", disk.ResName)
d.Set("role", disk.Role)
d.Set("sep_id", disk.SepID)
d.Set("sep_type", disk.SepType)
d.Set("size_max", disk.SizeMax)
d.Set("size_used", disk.SizeUsed)
d.Set("snapshots", flattendDiskSnapshotList(disk.Snapshots))
d.Set("status", disk.Status)
d.Set("tech_status", disk.TechStatus)
d.Set("type", disk.Type)
d.Set("vmid", disk.VMID)
return nil
}
func resourceDiskUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
if d.HasChange("size_max") {
oldSize, newSize := d.GetChange("size_max")
if oldSize.(int) < newSize.(int) {
log.Debugf("resourceDiskUpdate: resizing disk ID %s - %d GB -> %d GB",
d.Id(), oldSize.(int), newSize.(int))
urlValues.Add("diskId", d.Id())
urlValues.Add("size", fmt.Sprintf("%d", newSize.(int)))
_, err := c.DecortAPICall(ctx, "POST", disksResizeAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.Set("size_max", newSize)
} else if oldSize.(int) > newSize.(int) {
return diag.FromErr(fmt.Errorf("resourceDiskUpdate: Disk ID %s - reducing disk size is not allowed", d.Id()))
}
urlValues = &url.Values{}
}
if d.HasChange("disk_name") {
urlValues.Add("diskId", d.Id())
urlValues.Add("name", d.Get("disk_name").(string))
_, err := c.DecortAPICall(ctx, "POST", disksRenameAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
if d.HasChange("iotune") {
iot := d.Get("iotune").([]interface{})[0]
iotune := iot.(map[string]interface{})
urlValues.Add("diskId", d.Id())
urlValues.Add("iops", strconv.Itoa(iotune["total_iops_sec"].(int)))
urlValues.Add("read_bytes_sec", strconv.Itoa(iotune["read_bytes_sec"].(int)))
urlValues.Add("read_bytes_sec_max", strconv.Itoa(iotune["read_bytes_sec_max"].(int)))
urlValues.Add("read_iops_sec", strconv.Itoa(iotune["read_iops_sec"].(int)))
urlValues.Add("read_iops_sec_max", strconv.Itoa(iotune["read_iops_sec_max"].(int)))
urlValues.Add("size_iops_sec", strconv.Itoa(iotune["size_iops_sec"].(int)))
urlValues.Add("total_bytes_sec", strconv.Itoa(iotune["total_bytes_sec"].(int)))
urlValues.Add("total_bytes_sec_max", strconv.Itoa(iotune["total_bytes_sec_max"].(int)))
urlValues.Add("total_iops_sec_max", strconv.Itoa(iotune["total_iops_sec_max"].(int)))
urlValues.Add("write_bytes_sec", strconv.Itoa(iotune["write_bytes_sec"].(int)))
urlValues.Add("write_bytes_sec_max", strconv.Itoa(iotune["write_bytes_sec_max"].(int)))
urlValues.Add("write_iops_sec", strconv.Itoa(iotune["write_iops_sec"].(int)))
urlValues.Add("write_iops_sec_max", strconv.Itoa(iotune["write_iops_sec_max"].(int)))
_, err := c.DecortAPICall(ctx, "POST", disksIOLimitAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
if d.HasChange("restore") {
if d.Get("restore").(bool) {
urlValues.Add("diskId", d.Id())
urlValues.Add("reason", d.Get("reason").(string))
_, err := c.DecortAPICall(ctx, "POST", disksRestoreAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
}
}
return resourceDiskRead(ctx, d, m)
}
func resourceDiskDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
disk, err := utilityDiskCheckPresence(ctx, d, m)
if disk == nil {
if err != nil {
return diag.FromErr(err)
}
return nil
}
params := &url.Values{}
params.Add("diskId", d.Id())
params.Add("detach", strconv.FormatBool(d.Get("detach").(bool)))
params.Add("permanently", strconv.FormatBool(d.Get("permanently").(bool)))
params.Add("reason", d.Get("reason").(string))
c := m.(*controller.ControllerCfg)
_, err = c.DecortAPICall(ctx, "POST", disksDeleteAPI, params)
if err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceDiskExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
diskFacts, err := utilityDiskCheckPresence(ctx, d, m)
if diskFacts == nil {
if err != nil {
return false, err
}
return false, nil
}
return true, nil
}
func resourceDiskSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Required: true,
},
"disk_name": {
Type: schema.TypeString,
Required: true,
},
"size_max": {
Type: schema.TypeInt,
Required: true,
},
"gid": {
Type: schema.TypeInt,
Required: true,
},
"pool": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"sep_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"D", "B", "T"}, false),
},
"detach": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "detach disk from machine first",
},
"permanently": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "whether to completely delete the disk, works only with non attached disks",
},
"reason": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "reason for an action",
},
"restore": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "restore deleting disk",
},
"disk_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"acl": {
Type: schema.TypeString,
Computed: true,
},
"boot_partition": {
Type: schema.TypeInt,
Computed: true,
},
"compute_id": {
Type: schema.TypeInt,
Computed: true,
},
"compute_name": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"destruction_time": {
Type: schema.TypeInt,
Computed: true,
},
"devicename": {
Type: schema.TypeString,
Computed: true,
},
"disk_path": {
Type: schema.TypeString,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"image_id": {
Type: schema.TypeInt,
Computed: true,
},
"images": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"iotune": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"read_bytes_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"read_bytes_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"read_iops_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"read_iops_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"size_iops_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"total_bytes_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"total_bytes_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"total_iops_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"total_iops_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"write_bytes_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"write_bytes_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"write_iops_sec": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"write_iops_sec_max": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
},
},
},
"iqn": {
Type: schema.TypeString,
Computed: true,
},
"login": {
Type: schema.TypeString,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"order": {
Type: schema.TypeInt,
Computed: true,
},
"params": {
Type: schema.TypeString,
Computed: true,
},
"parent_id": {
Type: schema.TypeInt,
Computed: true,
},
"passwd": {
Type: schema.TypeString,
Computed: true,
},
"pci_slot": {
Type: schema.TypeInt,
Computed: true,
},
"purge_attempts": {
Type: schema.TypeInt,
Computed: true,
},
"purge_time": {
Type: schema.TypeInt,
Computed: true,
},
"reality_device_number": {
Type: schema.TypeInt,
Computed: true,
},
"reference_id": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"res_name": {
Type: schema.TypeString,
Computed: true,
},
"role": {
Type: schema.TypeString,
Computed: true,
},
"sep_type": {
Type: schema.TypeString,
Computed: true,
},
"size_used": {
Type: schema.TypeInt,
Computed: true,
},
"snapshots": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Computed: true,
},
"label": {
Type: schema.TypeString,
Computed: true,
},
"res_id": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_guid": {
Type: schema.TypeString,
Computed: true,
},
"snap_set_time": {
Type: schema.TypeInt,
Computed: true,
},
"timestamp": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"vmid": {
Type: schema.TypeInt,
Computed: true,
},
}
return rets
}
func ResourceDisk() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceDiskCreate,
ReadContext: resourceDiskRead,
UpdateContext: resourceDiskUpdate,
DeleteContext: resourceDiskDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout180s,
Read: &constants.Timeout30s,
Update: &constants.Timeout180s,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: resourceDiskSchemaMake(),
}
}

@ -0,0 +1,70 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package disks
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityDiskCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*Disk, error) {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
disk := &Disk{}
if d.Get("disk_id").(int) == 0 {
urlValues.Add("diskId", d.Id())
} else {
urlValues.Add("diskId", strconv.Itoa(d.Get("disk_id").(int)))
}
log.Debugf("utilityDiskCheckPresence: load disk")
diskRaw, err := c.DecortAPICall(ctx, "POST", disksGetAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(diskRaw), disk)
if err != nil {
return nil, err
}
return disk, nil
}

@ -0,0 +1,77 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package disks
import (
"context"
"encoding/json"
"net/url"
"strconv"
"strings"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityDiskListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (DisksList, error) {
diskList := DisksList{}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
if page, ok := d.GetOk("page"); ok {
urlValues.Add("page", strconv.Itoa(page.(int)))
}
if size, ok := d.GetOk("size"); ok {
urlValues.Add("size", strconv.Itoa(size.(int)))
}
if diskType, ok := d.GetOk("type"); ok {
urlValues.Add("type", strings.ToUpper(diskType.(string)))
}
if accountId, ok := d.GetOk("accountId"); ok {
urlValues.Add("accountId", strconv.Itoa(accountId.(int)))
}
log.Debugf("utilityDiskListCheckPresence: load disk list")
diskListRaw, err := c.DecortAPICall(ctx, "POST", disksListAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(diskListRaw), &diskList)
if err != nil {
return nil, err
}
return diskList, nil
}

@ -0,0 +1,49 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package k8s
const K8sCreateAPI = "/restmachine/cloudbroker/k8s/create"
const K8sGetAPI = "/restmachine/cloudbroker/k8s/get"
const K8sUpdateAPI = "/restmachine/cloudbroker/k8s/update"
const K8sDeleteAPI = "/restmachine/cloudbroker/k8s/delete"
const K8sWgCreateAPI = "/restmachine/cloudbroker/k8s/workersGroupAdd"
const K8sWgDeleteAPI = "/restmachine/cloudbroker/k8s/workersGroupDelete"
const K8sWorkerAddAPI = "/restmachine/cloudbroker/k8s/workerAdd"
const K8sWorkerDeleteAPI = "/restmachine/cloudbroker/k8s/deleteWorkerFromGroup"
const K8sGetConfigAPI = "/restmachine/cloudbroker/k8s/getConfig"
const LbGetAPI = "/restmachine/cloudbroker/lb/get"
const AsyncTaskGetAPI = "/restmachine/cloudbroker/tasks/get"

@ -0,0 +1,131 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package k8s
import (
"encoding/json"
"fmt"
"strconv"
)
type K8sNodeRecord struct {
ID int `json:"id"`
Name string `json:"name"`
Disk int `json:"disk"`
Cpu int `json:"cpu"`
Num int `json:"num"`
Ram int `json:"ram"`
DetailedInfo []struct {
ID int `json:"id"`
Name string `json:"name"`
} `json:"detailedInfo"`
}
//K8sRecord represents k8s instance
type K8sRecord struct {
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
CI int `json:"ciId"`
ID int `json:"id"`
Groups struct {
Masters K8sNodeRecord `json:"masters"`
Workers []K8sNodeRecord `json:"workers"`
} `json:"k8sGroups"`
LbID int `json:"lbId"`
Name string `json:"name"`
RgID int `json:"rgId"`
RgName string `json:"rgName"`
}
//LbRecord represents load balancer instance
type LbRecord struct {
ID int `json:"id"`
Name string `json:"name"`
RgID int `json:"rgId"`
VinsID int `json:"vinsId"`
ExtNetID int `json:"extnetId"`
PrimaryNode struct {
BackendIP string `json:"backendIp"`
ComputeID int `json:"computeId"`
FrontendIP string `json:"frontendIp"`
NetworkID int `json:"networkId"`
} `json:"primaryNode"`
}
//Blasphemous workaround for parsing Result value
type TaskResult int
func (r *TaskResult) UnmarshalJSON(b []byte) error {
if b[0] == '"' {
b := b[1 : len(b)-1]
if len(b) == 0 {
*r = 0
return nil
}
n, err := strconv.Atoi(string(b))
if err != nil {
return err
}
*r = TaskResult(n)
} else if b[0] == '[' {
res := []interface{}{}
if err := json.Unmarshal(b, &res); err != nil {
return err
}
if n, ok := res[0].(float64); ok {
*r = TaskResult(n)
} else {
return fmt.Errorf("could not unmarshal %v into int", res[0])
}
}
return nil
}
//AsyncTask represents a long task completion status
type AsyncTask struct {
AuditID string `json:"auditId"`
Completed bool `json:"completed"`
Error string `json:"error"`
Log []string `json:"log"`
Result TaskResult `json:"result"`
Stage string `json:"stage"`
Status string `json:"status"`
UpdateTime uint64 `json:"updateTime"`
UpdatedTime uint64 `json:"updatedTime"`
}
type SshKeyConfig struct {
User string
SshKey string
UserShell string
}

@ -0,0 +1,105 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package k8s
import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
func nodeMasterDefault() K8sNodeRecord {
return K8sNodeRecord{
Num: 1,
Cpu: 2,
Ram: 2048,
Disk: 0,
}
}
func nodeWorkerDefault() K8sNodeRecord {
return K8sNodeRecord{
Num: 1,
Cpu: 1,
Ram: 1024,
Disk: 0,
}
}
func parseNode(nodeList []interface{}) K8sNodeRecord {
node := nodeList[0].(map[string]interface{})
return K8sNodeRecord{
Num: node["num"].(int),
Cpu: node["cpu"].(int),
Ram: node["ram"].(int),
Disk: node["disk"].(int),
}
}
func nodeToResource(node K8sNodeRecord) []interface{} {
mp := make(map[string]interface{})
mp["num"] = node.Num
mp["cpu"] = node.Cpu
mp["ram"] = node.Ram
mp["disk"] = node.Disk
return []interface{}{mp}
}
func nodeK8sSubresourceSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"num": {
Type: schema.TypeInt,
Required: true,
Description: "Number of nodes to create.",
},
"cpu": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "Node CPU count.",
},
"ram": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "Node RAM in MB.",
},
"disk": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "Node boot disk size in GB.",
},
}
}

@ -0,0 +1,403 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
)
func resourceK8sCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sCreate: called with name %s, rg %d", d.Get("name").(string), d.Get("rg_id").(int))
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("name", d.Get("name").(string))
urlValues.Add("rgId", strconv.Itoa(d.Get("rg_id").(int)))
urlValues.Add("k8ciId", strconv.Itoa(d.Get("k8sci_id").(int)))
urlValues.Add("workerGroupName", d.Get("wg_name").(string))
var masterNode K8sNodeRecord
if masters, ok := d.GetOk("masters"); ok {
masterNode = parseNode(masters.([]interface{}))
} else {
masterNode = nodeMasterDefault()
}
urlValues.Add("masterNum", strconv.Itoa(masterNode.Num))
urlValues.Add("masterCpu", strconv.Itoa(masterNode.Cpu))
urlValues.Add("masterRam", strconv.Itoa(masterNode.Ram))
urlValues.Add("masterDisk", strconv.Itoa(masterNode.Disk))
var workerNode K8sNodeRecord
if workers, ok := d.GetOk("workers"); ok {
workerNode = parseNode(workers.([]interface{}))
} else {
workerNode = nodeWorkerDefault()
}
urlValues.Add("workerNum", strconv.Itoa(workerNode.Num))
urlValues.Add("workerCpu", strconv.Itoa(workerNode.Cpu))
urlValues.Add("workerRam", strconv.Itoa(workerNode.Ram))
urlValues.Add("workerDisk", strconv.Itoa(workerNode.Disk))
//if withLB, ok := d.GetOk("with_lb"); ok {
//urlValues.Add("withLB", strconv.FormatBool(withLB.(bool)))
//}
urlValues.Add("withLB", strconv.FormatBool(true))
if extNet, ok := d.GetOk("extnet_id"); ok {
urlValues.Add("extnetId", strconv.Itoa(extNet.(int)))
} else {
urlValues.Add("extnetId", "0")
}
//if desc, ok := d.GetOk("desc"); ok {
//urlValues.Add("desc", desc.(string))
//}
resp, err := c.DecortAPICall(ctx, "POST", K8sCreateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
urlValues = &url.Values{}
urlValues.Add("auditId", strings.Trim(resp, `"`))
for {
resp, err := c.DecortAPICall(ctx, "POST", AsyncTaskGetAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
task := AsyncTask{}
if err := json.Unmarshal([]byte(resp), &task); err != nil {
return diag.FromErr(err)
}
log.Debugf("resourceK8sCreate: instance creating - %s", task.Stage)
if task.Completed {
if task.Error != "" {
return diag.FromErr(fmt.Errorf("cannot create k8s instance: %v", task.Error))
}
d.SetId(strconv.Itoa(int(task.Result)))
break
}
time.Sleep(time.Second * 10)
}
k8s, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
d.Set("default_wg_id", k8s.Groups.Workers[0].ID)
urlValues = &url.Values{}
urlValues.Add("lbId", strconv.Itoa(k8s.LbID))
resp, err = c.DecortAPICall(ctx, "POST", LbGetAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
var lb LbRecord
if err := json.Unmarshal([]byte(resp), &lb); err != nil {
return diag.FromErr(err)
}
d.Set("extnet_id", lb.ExtNetID)
d.Set("lb_ip", lb.PrimaryNode.FrontendIP)
urlValues = &url.Values{}
urlValues.Add("k8sId", d.Id())
kubeconfig, err := c.DecortAPICall(ctx, "POST", K8sGetConfigAPI, urlValues)
if err != nil {
log.Warnf("could not get kubeconfig: %v", err)
}
d.Set("kubeconfig", kubeconfig)
return nil
}
func resourceK8sRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sRead: called with id %s, rg %d", d.Id(), d.Get("rg_id").(int))
k8s, err := utilityK8sCheckPresence(ctx, d, m)
if k8s == nil {
d.SetId("")
return diag.FromErr(err)
}
d.Set("name", k8s.Name)
d.Set("rg_id", k8s.RgID)
d.Set("k8sci_id", k8s.CI)
d.Set("wg_name", k8s.Groups.Workers[0].Name)
d.Set("masters", nodeToResource(k8s.Groups.Masters))
d.Set("workers", nodeToResource(k8s.Groups.Workers[0]))
d.Set("default_wg_id", k8s.Groups.Workers[0].ID)
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("lbId", strconv.Itoa(k8s.LbID))
resp, err := c.DecortAPICall(ctx, "POST", LbGetAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
var lb LbRecord
if err := json.Unmarshal([]byte(resp), &lb); err != nil {
return diag.FromErr(err)
}
d.Set("extnet_id", lb.ExtNetID)
d.Set("lb_ip", lb.PrimaryNode.FrontendIP)
urlValues = &url.Values{}
urlValues.Add("k8sId", d.Id())
kubeconfig, err := c.DecortAPICall(ctx, "POST", K8sGetConfigAPI, urlValues)
if err != nil {
log.Warnf("could not get kubeconfig: %v", err)
}
d.Set("kubeconfig", kubeconfig)
return nil
}
func resourceK8sUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sUpdate: called with id %s, rg %d", d.Id(), d.Get("rg_id").(int))
c := m.(*controller.ControllerCfg)
if d.HasChange("name") {
urlValues := &url.Values{}
urlValues.Add("k8sId", d.Id())
urlValues.Add("name", d.Get("name").(string))
_, err := c.DecortAPICall(ctx, "POST", K8sUpdateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
}
if d.HasChange("workers") {
k8s, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
wg := k8s.Groups.Workers[0]
urlValues := &url.Values{}
urlValues.Add("k8sId", d.Id())
urlValues.Add("workersGroupId", strconv.Itoa(wg.ID))
newWorkers := parseNode(d.Get("workers").([]interface{}))
if newWorkers.Num > wg.Num {
urlValues.Add("num", strconv.Itoa(newWorkers.Num-wg.Num))
if _, err := c.DecortAPICall(ctx, "POST", K8sWorkerAddAPI, urlValues); err != nil {
return diag.FromErr(err)
}
} else {
for i := wg.Num - 1; i >= newWorkers.Num; i-- {
urlValues.Set("workerId", strconv.Itoa(wg.DetailedInfo[i].ID))
if _, err := c.DecortAPICall(ctx, "POST", K8sWorkerDeleteAPI, urlValues); err != nil {
return diag.FromErr(err)
}
}
}
}
return nil
}
func resourceK8sDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sDelete: called with id %s, rg %d", d.Id(), d.Get("rg_id").(int))
k8s, err := utilityK8sCheckPresence(ctx, d, m)
if k8s == nil {
if err != nil {
return diag.FromErr(err)
}
return nil
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("k8sId", d.Id())
urlValues.Add("permanently", "true")
_, err = c.DecortAPICall(ctx, "POST", K8sDeleteAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceK8sExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
log.Debugf("resourceK8sExists: called with id %s, rg %d", d.Id(), d.Get("rg_id").(int))
k8s, err := utilityK8sCheckPresence(ctx, d, m)
if k8s == nil {
return false, err
}
return true, nil
}
func resourceK8sSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of the cluster.",
},
"rg_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "Resource group ID that this instance belongs to.",
},
"k8sci_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "ID of the k8s catalog item to base this instance on.",
},
"wg_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name for first worker group created with cluster.",
},
"masters": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: nodeK8sSubresourceSchemaMake(),
},
Description: "Master node(s) configuration.",
},
"workers": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: nodeK8sSubresourceSchemaMake(),
},
Description: "Worker node(s) configuration.",
},
//"with_lb": {
//Type: schema.TypeBool,
//Optional: true,
//ForceNew: true,
//Default: true,
//Description: "Create k8s with load balancer if true.",
//},
"extnet_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
Description: "ID of the external network to connect workers to. If omitted network will be chosen by the platfom.",
},
//"desc": {
//Type: schema.TypeString,
//Optional: true,
//Description: "Text description of this instance.",
//},
"lb_ip": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of default load balancer.",
},
"default_wg_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of default workers group for this instace.",
},
"kubeconfig": {
Type: schema.TypeString,
Computed: true,
Description: "Kubeconfig for cluster access.",
},
}
}
func ResourceK8s() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceK8sCreate,
ReadContext: resourceK8sRead,
UpdateContext: resourceK8sUpdate,
DeleteContext: resourceK8sDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout20m,
Read: &constants.Timeout30s,
Update: &constants.Timeout20m,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: resourceK8sSchemaMake(),
}
}

@ -0,0 +1,258 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"net/url"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
)
func resourceK8sWgCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sWgCreate: called with k8s id %d", d.Get("k8s_id").(int))
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int)))
urlValues.Add("name", d.Get("name").(string))
urlValues.Add("workerNum", strconv.Itoa(d.Get("num").(int)))
urlValues.Add("workerCpu", strconv.Itoa(d.Get("cpu").(int)))
urlValues.Add("workerRam", strconv.Itoa(d.Get("ram").(int)))
urlValues.Add("workerDisk", strconv.Itoa(d.Get("disk").(int)))
resp, err := c.DecortAPICall(ctx, "POST", K8sWgCreateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId(resp)
// This code is the supposed flow, but at the time of writing it's not yet implemented by the platfom
//urlValues = &url.Values{}
//urlValues.Add("auditId", strings.Trim(resp, `"`))
//for {
//resp, err := controller.decortAPICall("POST", AsyncTaskGetAPI, urlValues)
//if err != nil {
//return err
//}
//task := AsyncTask{}
//if err := json.Unmarshal([]byte(resp), &task); err != nil {
//return err
//}
//log.Debugf("resourceK8sCreate: workers group creating - %s", task.Stage)
//if task.Completed {
//if task.Error != "" {
//return fmt.Errorf("cannot create workers group: %v", task.Error)
//}
//d.SetId(strconv.Itoa(int(task.Result)))
//break
//}
//time.Sleep(time.Second * 5)
//}
return nil
}
func resourceK8sWgRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sWgRead: called with k8s id %d", d.Get("k8s_id").(int))
wg, err := utilityK8sWgCheckPresence(ctx, d, m)
if wg == nil {
d.SetId("")
return diag.FromErr(err)
}
d.Set("name", wg.Name)
d.Set("num", wg.Num)
d.Set("cpu", wg.Cpu)
d.Set("ram", wg.Ram)
d.Set("disk", wg.Disk)
return nil
}
func resourceK8sWgUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sWgUpdate: called with k8s id %d", d.Get("k8s_id").(int))
c := m.(*controller.ControllerCfg)
wg, err := utilityK8sWgCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
urlValues := &url.Values{}
urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int)))
urlValues.Add("workersGroupId", d.Id())
if newNum := d.Get("num").(int); newNum > wg.Num {
urlValues.Add("num", strconv.Itoa(newNum-wg.Num))
_, err := c.DecortAPICall(ctx, "POST", K8sWorkerAddAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
} else {
for i := wg.Num - 1; i >= newNum; i-- {
urlValues.Set("workerId", strconv.Itoa(wg.DetailedInfo[i].ID))
_, err := c.DecortAPICall(ctx, "POST", K8sWorkerDeleteAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
}
}
return nil
}
func resourceK8sWgDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sWgDelete: called with k8s id %d", d.Get("k8s_id").(int))
wg, err := utilityK8sWgCheckPresence(ctx, d, m)
if wg == nil {
if err != nil {
return diag.FromErr(err)
}
return nil
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int)))
urlValues.Add("workersGroupId", strconv.Itoa(wg.ID))
_, err = c.DecortAPICall(ctx, "POST", K8sWgDeleteAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceK8sWgExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
log.Debugf("resourceK8sWgExists: called with k8s id %d", d.Get("k8s_id").(int))
wg, err := utilityK8sWgCheckPresence(ctx, d, m)
if wg == nil {
if err != nil {
return false, err
}
return false, nil
}
return true, nil
}
func resourceK8sWgSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"k8s_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "ID of k8s instance.",
},
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name of the worker group.",
},
"num": {
Type: schema.TypeInt,
Optional: true,
Default: 1,
Description: "Number of worker nodes to create.",
},
"cpu": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Default: 1,
Description: "Worker node CPU count.",
},
"ram": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Default: 1024,
Description: "Worker node RAM in MB.",
},
"disk": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Default: 0,
Description: "Worker node boot disk size. If unspecified or 0, size is defined by OS image size.",
},
}
}
func ResourceK8sWg() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceK8sWgCreate,
ReadContext: resourceK8sWgRead,
UpdateContext: resourceK8sWgUpdate,
DeleteContext: resourceK8sWgDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout20m,
Read: &constants.Timeout30s,
Update: &constants.Timeout20m,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: resourceK8sWgSchemaMake(),
}
}

@ -0,0 +1,63 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"encoding/json"
"net/url"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/controller"
)
func utilityK8sCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*K8sRecord, error) {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("k8sId", d.Id())
resp, err := c.DecortAPICall(ctx, "POST", K8sGetAPI, urlValues)
if err != nil {
return nil, err
}
if resp == "" {
return nil, nil
}
var k8s K8sRecord
if err := json.Unmarshal([]byte(resp), &k8s); err != nil {
return nil, err
}
return &k8s, nil
}

@ -0,0 +1,75 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/controller"
)
func utilityK8sWgCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*K8sNodeRecord, error) {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int)))
resp, err := c.DecortAPICall(ctx, "POST", K8sGetAPI, urlValues)
if err != nil {
return nil, err
}
if resp == "" {
return nil, nil
}
var k8s K8sRecord
if err := json.Unmarshal([]byte(resp), &k8s); err != nil {
return nil, err
}
id, err := strconv.Atoi(d.Id())
if err != nil {
return nil, err
}
for _, wg := range k8s.Groups.Workers {
if wg.ID == id {
return &wg, nil
}
}
return nil, nil
}

@ -0,0 +1,46 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package kvmvm
const KvmX86CreateAPI = "/restmachine/cloudbroker/kvmx86/create"
const KvmPPCCreateAPI = "/restmachine/cloudbroker/kvmppc/create"
const ComputeGetAPI = "/restmachine/cloudbroker/compute/get"
const RgListComputesAPI = "/restmachine/cloudbroker/rg/listComputes"
const ComputeNetAttachAPI = "/restmachine/cloudbroker/compute/netAttach"
const ComputeNetDetachAPI = "/restmachine/cloudbroker/compute/netDetach"
const ComputeDiskAttachAPI = "/restmachine/cloudbroker/compute/diskAttach"
const ComputeDiskDetachAPI = "/restmachine/cloudbroker/compute/diskDetach"
const ComputeStartAPI = "/restmachine/cloudbroker/compute/start"
const ComputeStopAPI = "/restmachine/cloudbroker/compute/stop"
const ComputeResizeAPI = "/restmachine/cloudbroker/compute/resize"
const DisksResizeAPI = "/restmachine/cloudbroker/disks/resize2"
const ComputeDeleteAPI = "/restmachine/cloudbroker/compute/delete"

@ -0,0 +1,385 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package kvmvm
import (
"context"
"encoding/json"
"errors"
"fmt"
// "net/url"
"github.com/rudecs/terraform-provider-decort/internal/constants"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
// "github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)
// Parse list of all disks from API compute/get into a list of "extra disks" attached to this compute
// Extra disks are all compute disks but a boot disk.
func parseComputeDisksToExtraDisks(disks []DiskRecord) []interface{} {
// this return value will be used to d.Set("extra_disks",) item of dataSourceCompute schema,
// which is a simple list of integer disk IDs excluding boot disk ID
length := len(disks)
log.Debugf("parseComputeDisksToExtraDisks: called for %d disks", length)
if length == 0 || (length == 1 && disks[0].Type == "B") {
// the disk list is empty (which is kind of strange - diskless compute?), or
// there is only one disk in the list and it is a boot disk;
// as we skip boot disks, the result will be of 0 length anyway
return make([]interface{}, 0)
}
result := make([]interface{}, length-1)
idx := 0
for _, value := range disks {
if value.Type == "B" {
// skip boot disk when iterating over the list of disks
continue
}
result[idx] = value.ID
idx++
}
return result
}
func parseBootDiskSize(disks []DiskRecord) int {
// this return value will be used to d.Set("boot_disk_size",) item of dataSourceCompute schema
if len(disks) == 0 {
return 0
}
for _, value := range disks {
if value.Type == "B" {
return value.SizeMax
}
}
return 0
}
func parseBootDiskId(disks []DiskRecord) uint {
// this return value will be used to d.Set("boot_disk_id",) item of dataSourceCompute schema
if len(disks) == 0 {
return 0
}
for _, value := range disks {
if value.Type == "B" {
return value.ID
}
}
return 0
}
func findBootDisk(disks []DiskRecord) (*DiskRecord, error) {
for _, d := range disks {
if d.Type == "B" {
return &d, nil
}
}
return nil, errors.New("boot disk not found")
}
// Parse the list of interfaces from compute/get response into a list of networks
// attached to this compute
func parseComputeInterfacesToNetworks(ifaces []InterfaceRecord) []interface{} {
// return value will be used to d.Set("network") item of dataSourceCompute schema
length := len(ifaces)
log.Debugf("parseComputeInterfacesToNetworks: called for %d ifaces", length)
result := []interface{}{}
for _, value := range ifaces {
elem := make(map[string]interface{})
// Keys in this map should correspond to the Schema definition
// as returned by networkSubresourceSchemaMake()
elem["net_id"] = value.NetID
elem["net_type"] = value.NetType
elem["ip_address"] = value.IPAddress
elem["mac"] = value.MAC
// log.Debugf(" element %d: net_id=%d, net_type=%s", i, value.NetID, value.NetType)
result = append(result, elem)
}
return result
}
func flattenCompute(d *schema.ResourceData, compFacts string) error {
// This function expects that compFacts string contains response from API compute/get,
// i.e. detailed information about compute instance.
//
// NOTE: this function modifies ResourceData argument - as such it should never be called
// from resourceComputeExists(...) method
model := ComputeGetResp{}
log.Debugf("flattenCompute: ready to unmarshal string %s", compFacts)
err := json.Unmarshal([]byte(compFacts), &model)
if err != nil {
return err
}
log.Debugf("flattenCompute: ID %d, RG ID %d", model.ID, model.RgID)
d.SetId(fmt.Sprintf("%d", model.ID))
// d.Set("compute_id", model.ID) - we should NOT set compute_id in the schema here: if it was set - it is already set, if it wasn't - we shouldn't
d.Set("name", model.Name)
d.Set("rg_id", model.RgID)
d.Set("rg_name", model.RgName)
d.Set("account_id", model.AccountID)
d.Set("account_name", model.AccountName)
d.Set("driver", model.Driver)
d.Set("cpu", model.Cpu)
d.Set("ram", model.Ram)
// d.Set("boot_disk_size", model.BootDiskSize) - bootdiskSize key in API compute/get is always zero, so we set boot_disk_size in another way
d.Set("image_id", model.ImageID)
d.Set("description", model.Desc)
d.Set("cloud_init", "applied") // NOTE: for existing compute we hard-code this value as an indicator for DiffSuppress fucntion
// d.Set("status", model.Status)
// d.Set("tech_status", model.TechStatus)
if model.TechStatus == "STARTED" {
d.Set("started", true)
} else {
d.Set("started", false)
}
bootDisk, err := findBootDisk(model.Disks)
if err != nil {
return err
}
d.Set("boot_disk_size", bootDisk.SizeMax)
d.Set("boot_disk_id", bootDisk.ID) // we may need boot disk ID in resize operations
d.Set("sep_id", bootDisk.SepID)
d.Set("pool", bootDisk.Pool)
if len(model.Disks) > 0 {
log.Debugf("flattenCompute: calling parseComputeDisksToExtraDisks for %d disks", len(model.Disks))
if err = d.Set("extra_disks", parseComputeDisksToExtraDisks(model.Disks)); err != nil {
return err
}
}
if len(model.Interfaces) > 0 {
log.Debugf("flattenCompute: calling parseComputeInterfacesToNetworks for %d interfaces", len(model.Interfaces))
if err = d.Set("network", parseComputeInterfacesToNetworks(model.Interfaces)); err != nil {
return err
}
}
if len(model.OsUsers) > 0 {
log.Debugf("flattenCompute: calling parseOsUsers for %d logins", len(model.OsUsers))
if err = d.Set("os_users", parseOsUsers(model.OsUsers)); err != nil {
return err
}
}
return nil
}
func dataSourceComputeRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
compFacts, err := utilityComputeCheckPresence(ctx, d, m)
if compFacts == "" {
// if empty string is returned from utilityComputeCheckPresence then there is no
// such Compute and err tells so - just return it to the calling party
d.SetId("") // ensure ID is empty
return diag.FromErr(err)
}
if err = flattenCompute(d, compFacts); err != nil {
return diag.FromErr(err)
}
return nil
}
func DataSourceCompute() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceComputeRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of this compute instance. NOTE: this parameter is case sensitive.",
},
// TODO: consider removing compute_id from the schema, as it not practical to call this data provider if
// corresponding compute ID is already known
"compute_id": {
Type: schema.TypeInt,
Optional: true,
Description: "ID of the compute instance. If ID is specified, name and resource group ID are ignored.",
},
"rg_id": {
Type: schema.TypeInt,
Optional: true,
Description: "ID of the resource group where this compute instance is located.",
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the resource group where this compute instance is located.",
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the account this compute instance belongs to.",
},
"account_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the account this compute instance belongs to.",
},
"driver": {
Type: schema.TypeString,
Computed: true,
Description: "Hardware architecture of this compute instance.",
},
"cpu": {
Type: schema.TypeInt,
Computed: true,
Description: "Number of CPUs allocated for this compute instance.",
},
"ram": {
Type: schema.TypeInt,
Computed: true,
Description: "Amount of RAM in MB allocated for this compute instance.",
},
"image_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the OS image this compute instance is based on.",
},
"image_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the OS image this compute instance is based on.",
},
"boot_disk_size": {
Type: schema.TypeInt,
Computed: true,
Description: "This compute instance boot disk size in GB.",
},
"boot_disk_id": {
Type: schema.TypeInt,
Computed: true,
Description: "This compute instance boot disk ID.",
},
"extra_disks": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Description: "IDs of the extra disk(s) attached to this compute.",
},
/*
"disks": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: dataSourceDiskSchemaMake(), // ID, type, name, size, account ID, SEP ID, SEP type, pool, status, tech status, compute ID, image ID
},
Description: "Detailed specification for all disks attached to this compute instance (including bood disk).",
},
*/
"network": {
Type: schema.TypeSet,
Optional: true,
MaxItems: constants.MaxNetworksPerCompute,
Elem: &schema.Resource{
Schema: networkSubresourceSchemaMake(),
},
Description: "Network connection(s) for this compute.",
},
"os_users": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: osUsersSubresourceSchemaMake(),
},
Description: "Guest OS users provisioned on this compute instance.",
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: "User-defined text description of this compute instance.",
},
"cloud_init": {
Type: schema.TypeString,
Computed: true,
Description: "Placeholder for cloud_init parameters.",
},
"started": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Is compute started.",
},
},
}
}

@ -0,0 +1,190 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package kvmvm
type DiskRecord struct {
Acl map[string]interface{} `json:"acl"`
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
BootPartition int `json:"bootPartition"`
CreatedTime uint64 `json:"creationTime"`
ComputeID int `json:"computeId"`
ComputeName string `json:"computeName"`
DeletedTime uint64 `json:"deletionTime"`
DeviceName string `json:"devicename"`
Desc string `json:"desc"`
DestructionTime uint64 `json:"destructionTime"`
DiskPath string `json:"diskPath"`
GridID int `json:"gid"`
GUID int `json:"guid"`
ID uint `json:"id"`
ImageID int `json:"imageId"`
Images []int `json:"images"`
IOTune map[string]interface{} `json:"iotune"`
IQN string `json:"iqn"`
Login string `json:"login"`
Name string `json:"name"`
MachineId int `json:"machineId"`
MachineName string `json:"machineName"`
Milestones uint64 `json:"milestones"`
Order int `json:"order"`
Params string `json:"params"`
Passwd string `json:"passwd"`
ParentId int `json:"parentId"`
PciSlot int `json:"pciSlot"`
Pool string `json:"pool"`
PurgeTime uint64 `json:"purgeTime"`
PurgeAttempts uint64 `json:"purgeAttempts"`
RealityDeviceNumber int `json:"realityDeviceNumber"`
ReferenceId string `json:"referenceId"`
ResID string `json:"resId"`
ResName string `json:"resName"`
Role string `json:"role"`
SepType string `json:"sepType"`
SepID int `json:"sepId"` // NOTE: absent from compute/get output
SizeMax int `json:"sizeMax"`
SizeUsed int `json:"sizeUsed"` // sum over all snapshots of this disk to report total consumed space
Snapshots []SnapshotRecord `json:"snapshots"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
Type string `json:"type"`
UpdateBy uint64 `json:"updateBy"`
VMID int `json:"vmid"`
}
type InterfaceRecord struct {
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"` // 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 InterfaceQosRecord struct {
ERate int `json:"eRate"`
Guid string `json:"guid"`
InBurst int `json:"inBurst"`
InRate int `json:"inRate"`
}
type SnapshotRecord struct {
Guid string `json:"guid"`
Label string `json:"label"`
ResId string `json:"resId"`
SnapSetGuid string `json:"snapSetGuid"`
SnapSetTime uint64 `json:"snapSetTime"`
TimeStamp uint64 `json:"timestamp"`
}
type SnapshotRecordList []SnapshotRecord
type ComputeGetResp struct {
// ACLs `json:"ACL"` - it is a dictionary, special parsing required
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
Arch string `json:"arch"`
BootDiskSize int `json:"bootdiskSize"`
CloneReference int `json:"cloneReference"`
Clones []int `json:"clones"`
Cpu int `json:"cpus"`
Desc string `json:"desc"`
Disks []DiskRecord `json:"disks"`
Driver string `json:"driver"`
GridID int `json:"gid"`
ID uint `json:"id"`
ImageID int `json:"imageId"`
ImageName string `json:"imageName"`
Interfaces []InterfaceRecord `json:"interfaces"`
LockStatus string `json:"lockStatus"`
ManagerID int `json:"managerId"`
ManagerType string `json:"manageType"`
Name string `json:"name"`
NatableVinsID int `json:"natableVinsId"`
NatableVinsIP string `json:"natableVinsIp"`
NatableVinsName string `json:"natableVinsName"`
NatableVinsNet string `json:"natableVinsNetwork"`
NatableVinsNetName string `json:"natableVinsNetworkName"`
OsUsers []OsUserRecord `json:"osUsers"`
Ram int `json:"ram"`
RgID int `json:"rgId"`
RgName string `json:"rgName"`
SnapSets []SnapSetRecord `json:"snapSets"`
Status string `json:"status"`
// Tags []string `json:"tags"` // Tags were reworked since DECORT 3.7.1
TechStatus string `json:"techStatus"`
TotalDiskSize int `json:"totalDiskSize"`
UpdatedBy string `json:"updatedBy"`
UpdateTime uint64 `json:"updateTime"`
UserManaged bool `json:"userManaged"`
Vgpus []int `json:"vgpus"`
VinsConnected int `json:"vinsConnected"`
VirtualImageID int `json:"virtualImageId"`
}
type OsUserRecord struct {
Guid string `json:"guid"`
Login string `json:"login"`
Password string `json:"password"`
PubKey string `json:"pubkey"`
}
type SnapSetRecord struct {
Disks []int `json:"disks"`
Guid string `json:"guid"`
Label string `json:"label"`
TimeStamp uint64 `json:"timestamp"`
}
type ComputeBriefRecord struct { // this is a brief compute specifiaction as returned by API rg/listComputes
// we do not even include here all fields as returned by this API, but only the most important that
// are really necessary to identify and distinguish computes
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
Name string `json:"name"`
ID uint `json:"id"`
RgID int `json:"rgId"`
RgName string `json:"rgName"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
}
type RgListComputesResp []ComputeBriefRecord

@ -0,0 +1,154 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package kvmvm
import (
"bytes"
"hash/fnv"
"github.com/rudecs/terraform-provider-decort/internal/statefuncs"
log "github.com/sirupsen/logrus"
"sort"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
// This is subresource of compute resource used when creating/managing compute network connections
func networkSubresIPAddreDiffSupperss(key, oldVal, newVal string, d *schema.ResourceData) bool {
if newVal != "" && newVal != oldVal {
log.Debugf("networkSubresIPAddreDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=FALSE", key, oldVal, newVal)
return false
}
log.Debugf("networkSubresIPAddreDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=TRUE", key, oldVal, newVal)
return true // suppress difference
}
// This function is based on the original Terraform SerializeResourceForHash found
// in helper/schema/serialize.go
// It skips network subresource attributes, which are irrelevant for identification
// of unique network blocks
func networkSubresourceSerialize(output *bytes.Buffer, val interface{}, resource *schema.Resource) {
if val == nil {
return
}
rs := resource.Schema
m := val.(map[string]interface{})
keys := make([]string, 0, len(rs))
allComputed := true
for k, val := range rs {
if val.Optional || val.Required {
allComputed = false
}
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
// explicitly ignore "ip_address" when hashing
if k == "ip_address" {
continue
}
subSchema := rs[k]
// Skip attributes that are not user-provided. Computed attributes
// do not contribute to the hash since their ultimate value cannot
// be known at plan/diff time.
if !allComputed && !(subSchema.Required || subSchema.Optional) {
continue
}
output.WriteString(k)
output.WriteRune(':')
value := m[k]
schema.SerializeValueForHash(output, value, subSchema)
}
}
// HashNetworkSubresource hashes network subresource of compute resource. It uses
// specially designed networkSubresourceSerialize (see above) to make sure hashing
// does not involve attributes that we deem irrelevant to the uniqueness of network
// subresource definitions.
// It is this function that should be specified as SchemaSetFunc when creating Set
// from network subresource (e.g. in flattenCompute)
//
// This function is based on the original Terraform function HashResource from
// helper/schema/set.go
func HashNetworkSubresource(resource *schema.Resource) schema.SchemaSetFunc {
return func(v interface{}) int {
var serialized bytes.Buffer
networkSubresourceSerialize(&serialized, v, resource)
hs := fnv.New32a()
hs.Write(serialized.Bytes())
return int(hs.Sum32())
}
}
func networkSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"net_type": {
Type: schema.TypeString,
Required: true,
StateFunc: statefuncs.StateFuncToUpper,
ValidateFunc: validation.StringInSlice([]string{"EXTNET", "VINS"}, false), // observe case while validating
Description: "Type of the network for this connection, either EXTNET or VINS.",
},
"net_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the network for this connection.",
},
"ip_address": {
Type: schema.TypeString,
Optional: true,
Computed: true,
DiffSuppressFunc: networkSubresIPAddreDiffSupperss,
Description: "Optional IP address to assign to this connection. This IP should belong to the selected network and free for use.",
},
"mac": {
Type: schema.TypeString,
Computed: true,
Description: "MAC address associated with this connection. MAC address is assigned automatically.",
},
}
return rets
}

@ -0,0 +1,86 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package kvmvm
import (
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func parseOsUsers(logins []OsUserRecord) []interface{} {
var result = make([]interface{}, len(logins))
for index, value := range logins {
elem := make(map[string]interface{})
elem["guid"] = value.Guid
elem["login"] = value.Login
elem["password"] = value.Password
elem["public_key"] = value.PubKey
result[index] = elem
log.Debugf("parseOsUsers: parsed element %d - login %q", index, value.Login)
}
return result
}
func osUsersSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Computed: true,
Description: "GUID of this guest OS user.",
},
"login": {
Type: schema.TypeString,
Computed: true,
Description: "Login name of this guest OS user.",
},
"password": {
Type: schema.TypeString,
Computed: true,
Sensitive: true,
Description: "Password of this guest OS user.",
},
"public_key": {
Type: schema.TypeString,
Computed: true,
Description: "SSH public key of this guest OS user.",
},
}
return rets
}

@ -0,0 +1,542 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package kvmvm
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
"github.com/rudecs/terraform-provider-decort/internal/statefuncs"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func cloudInitDiffSupperss(key, oldVal, newVal string, d *schema.ResourceData) bool {
if oldVal == "" && newVal != "applied" {
// if old value for "cloud_init" resource is empty string, it means that we are creating new compute
// and there is a chance that the user will want custom cloud init parameters - so we check if
// cloud_init is explicitly set in TF file by making sure that its new value is different from "applied",
// which is a reserved key word.
log.Debugf("cloudInitDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=FALSE", key, oldVal, newVal)
return false // there is a difference between stored and new value
}
log.Debugf("cloudInitDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=TRUE", key, oldVal, newVal)
return true // suppress difference
}
func resourceComputeCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// we assume all mandatory parameters it takes to create a comptue instance are properly
// specified - we rely on schema "Required" attributes to let Terraform validate them for us
log.Debugf("resourceComputeCreate: called for Compute name %q, RG ID %d", d.Get("name").(string), d.Get("rg_id").(int))
// create basic Compute (i.e. without extra disks and network connections - those will be attached
// by subsequent individual API calls).
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("rgId", fmt.Sprintf("%d", d.Get("rg_id").(int)))
urlValues.Add("name", d.Get("name").(string))
urlValues.Add("cpu", fmt.Sprintf("%d", d.Get("cpu").(int)))
urlValues.Add("ram", fmt.Sprintf("%d", d.Get("ram").(int)))
urlValues.Add("imageId", fmt.Sprintf("%d", d.Get("image_id").(int)))
urlValues.Add("bootDisk", fmt.Sprintf("%d", d.Get("boot_disk_size").(int)))
urlValues.Add("netType", "NONE") // at the 1st step create isolated compute
urlValues.Add("start", "0") // at the 1st step create compute in a stopped state
argVal, argSet := d.GetOk("description")
if argSet {
urlValues.Add("desc", argVal.(string))
}
if sepID, ok := d.GetOk("sep_id"); ok {
urlValues.Add("sepId", strconv.Itoa(sepID.(int)))
}
if pool, ok := d.GetOk("pool"); ok {
urlValues.Add("pool", pool.(string))
}
/*
sshKeysVal, sshKeysSet := d.GetOk("ssh_keys")
if sshKeysSet {
// process SSH Key settings and set API values accordingly
log.Debugf("resourceComputeCreate: calling makeSshKeysArgString to setup SSH keys for guest login(s)")
urlValues.Add("userdata", makeSshKeysArgString(sshKeysVal.([]interface{})))
}
*/
computeCreateAPI := KvmX86CreateAPI
driver := d.Get("driver").(string)
if driver == "KVM_PPC" {
computeCreateAPI = KvmPPCCreateAPI
log.Debugf("resourceComputeCreate: creating Compute of type KVM VM PowerPC")
} else { // note that we do not validate arch value for explicit "KVM_X86" here
log.Debugf("resourceComputeCreate: creating Compute of type KVM VM x86")
}
argVal, argSet = d.GetOk("cloud_init")
if argSet {
// userdata must not be empty string and must not be a reserved keyword "applied"
userdata := argVal.(string)
if userdata != "" && userdata != "applied" {
urlValues.Add("userdata", userdata)
}
}
apiResp, err := c.DecortAPICall(ctx, "POST", computeCreateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
// Compute create API returns ID of the new Compute instance on success
d.SetId(apiResp) // update ID of the resource to tell Terraform that the resource exists, albeit partially
compId, _ := strconv.Atoi(apiResp)
log.Debugf("resourceComputeCreate: new simple Compute ID %d, name %s created", compId, d.Get("name").(string))
// Configure data disks if any
argVal, argSet = d.GetOk("extra_disks")
if argSet && argVal.(*schema.Set).Len() > 0 {
// urlValues.Add("desc", argVal.(string))
log.Debugf("resourceComputeCreate: calling utilityComputeExtraDisksConfigure to attach %d extra disk(s)", argVal.(*schema.Set).Len())
err = utilityComputeExtraDisksConfigure(ctx, d, m, false) // do_delta=false, as we are working on a new compute
if err != nil {
log.Errorf("resourceComputeCreate: error when attaching extra disk(s) to a new Compute ID %d: %v", compId, err)
return diag.FromErr(err)
}
}
// Configure external networks if any
argVal, argSet = d.GetOk("network")
if argSet && argVal.(*schema.Set).Len() > 0 {
log.Debugf("resourceComputeCreate: calling utilityComputeNetworksConfigure to attach %d network(s)", argVal.(*schema.Set).Len())
err = utilityComputeNetworksConfigure(ctx, d, m, false) // do_delta=false, as we are working on a new compute
if err != nil {
log.Errorf("resourceComputeCreate: error when attaching networks to a new Compute ID %d: %s", compId, err)
return diag.FromErr(err)
}
}
// Note bene: we created compute in a STOPPED state (this is required to properly attach 1st network interface),
// now we need to start it before we report the sequence complete
if d.Get("started").(bool) {
reqValues := &url.Values{}
reqValues.Add("computeId", fmt.Sprintf("%d", compId))
log.Debugf("resourceComputeCreate: starting Compute ID %d after completing its resource configuration", compId)
if _, err := c.DecortAPICall(ctx, "POST", ComputeStartAPI, reqValues); err != nil {
return diag.FromErr(err)
}
}
log.Debugf("resourceComputeCreate: new Compute ID %d, name %s creation sequence complete", compId, d.Get("name").(string))
// We may reuse dataSourceComputeRead here as we maintain similarity
// between Compute resource and Compute data source schemas
// Compute read function will also update resource ID on success, so that Terraform
// will know the resource exists
return dataSourceComputeRead(ctx, d, m)
}
func resourceComputeRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceComputeRead: called for Compute name %s, RG ID %d",
d.Get("name").(string), d.Get("rg_id").(int))
compFacts, err := utilityComputeCheckPresence(ctx, d, m)
if compFacts == "" {
if err != nil {
return diag.FromErr(err)
}
// Compute with such name and RG ID was not found
return nil
}
if err = flattenCompute(d, compFacts); err != nil {
return diag.FromErr(err)
}
log.Debugf("resourceComputeRead: after flattenCompute: Compute ID %s, name %q, RG ID %d",
d.Id(), d.Get("name").(string), d.Get("rg_id").(int))
return nil
}
func resourceComputeUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceComputeUpdate: called for Compute ID %s / name %s, RGID %d",
d.Id(), d.Get("name").(string), d.Get("rg_id").(int))
c := m.(*controller.ControllerCfg)
/*
1. Resize CPU/RAM
2. Resize (grow) boot disk
3. Update extra disks
4. Update networks
5. Start/stop
*/
// 1. Resize CPU/RAM
params := &url.Values{}
doUpdate := false
params.Add("computeId", d.Id())
oldCpu, newCpu := d.GetChange("cpu")
if oldCpu.(int) != newCpu.(int) {
params.Add("cpu", fmt.Sprintf("%d", newCpu.(int)))
doUpdate = true
} else {
params.Add("cpu", "0") // no change to CPU allocation
}
oldRam, newRam := d.GetChange("ram")
if oldRam.(int) != newRam.(int) {
params.Add("ram", fmt.Sprintf("%d", newRam.(int)))
doUpdate = true
} else {
params.Add("ram", "0")
}
if doUpdate {
log.Debugf("resourceComputeUpdate: changing CPU %d -> %d and/or RAM %d -> %d",
oldCpu.(int), newCpu.(int),
oldRam.(int), newRam.(int))
params.Add("force", "true")
_, err := c.DecortAPICall(ctx, "POST", ComputeResizeAPI, params)
if err != nil {
return diag.FromErr(err)
}
}
// 2. Resize (grow) Boot disk
oldSize, newSize := d.GetChange("boot_disk_size")
if oldSize.(int) < newSize.(int) {
bdsParams := &url.Values{}
bdsParams.Add("diskId", fmt.Sprintf("%d", d.Get("boot_disk_id").(int)))
bdsParams.Add("size", fmt.Sprintf("%d", newSize.(int)))
log.Debugf("resourceComputeUpdate: compute ID %s, boot disk ID %d resize %d -> %d",
d.Id(), d.Get("boot_disk_id").(int), oldSize.(int), newSize.(int))
_, err := c.DecortAPICall(ctx, "POST", DisksResizeAPI, bdsParams)
if err != nil {
return diag.FromErr(err)
}
} else if oldSize.(int) > newSize.(int) {
log.Warnf("resourceComputeUpdate: compute ID %s - shrinking boot disk is not allowed", d.Id())
}
// 3. Calculate and apply changes to data disks
err := utilityComputeExtraDisksConfigure(ctx, d, m, true) // pass do_delta = true to apply changes, if any
if err != nil {
return diag.FromErr(err)
}
// 4. Calculate and apply changes to network connections
err = utilityComputeNetworksConfigure(ctx, d, m, true) // pass do_delta = true to apply changes, if any
if err != nil {
return diag.FromErr(err)
}
if d.HasChange("started") {
params := &url.Values{}
params.Add("computeId", d.Id())
if d.Get("started").(bool) {
if _, err := c.DecortAPICall(ctx, "POST", ComputeStartAPI, params); err != nil {
return diag.FromErr(err)
}
} else {
if _, err := c.DecortAPICall(ctx, "POST", ComputeStopAPI, params); err != nil {
return diag.FromErr(err)
}
}
}
// we may reuse dataSourceComputeRead here as we maintain similarity
// between Compute resource and Compute data source schemas
return dataSourceComputeRead(ctx, d, m)
}
func resourceComputeDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// NOTE: this function destroys target Compute instance "permanently", so
// there is no way to restore it.
// If compute being destroyed has some extra disks attached, they are
// detached from the compute
log.Debugf("resourceComputeDelete: called for Compute name %s, RG ID %d",
d.Get("name").(string), d.Get("rg_id").(int))
compFacts, err := utilityComputeCheckPresence(ctx, d, m)
if compFacts == "" {
if err != nil {
return diag.FromErr(err)
}
// the target Compute does not exist - in this case according to Terraform best practice
// we exit from Destroy method without error
return nil
}
c := m.(*controller.ControllerCfg)
model := ComputeGetResp{}
log.Debugf("resourceComputeDelete: ready to unmarshal string %s", compFacts)
err = json.Unmarshal([]byte(compFacts), &model)
if err == nil && len(model.Disks) > 0 {
// prepare to detach data disks from compute - do it only if compFacts unmarshalled
// properly and the resulting model contains non-empty Disks list
for _, diskFacts := range model.Disks {
if diskFacts.Type == "B" {
// boot disk is never detached on compute delete
continue
}
log.Debugf("resourceComputeDelete: ready to detach data disk ID %d from compute ID %s", diskFacts.ID, d.Id())
detachParams := &url.Values{}
detachParams.Add("computeId", d.Id())
detachParams.Add("diskId", fmt.Sprintf("%d", diskFacts.ID))
_, err = c.DecortAPICall(ctx, "POST", ComputeDiskDetachAPI, detachParams)
if err != nil {
// We do not fail compute deletion on data disk detach errors
log.Errorf("resourceComputeDelete: error when detaching Disk ID %d: %s", diskFacts.ID, err)
}
}
}
params := &url.Values{}
params.Add("computeId", d.Id())
params.Add("permanently", "1")
// TODO: this is for the upcoming API update - params.Add("detachdisks", "1")
_, err = c.DecortAPICall(ctx, "POST", ComputeDeleteAPI, params)
if err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceComputeExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
// Reminder: according to Terraform rules, this function should not modify its ResourceData argument
log.Debugf("resourceComputeExist: called for Compute name %s, RG ID %d",
d.Get("name").(string), d.Get("rg_id").(int))
compFacts, err := utilityComputeCheckPresence(ctx, d, m)
if compFacts == "" {
if err != nil {
return false, err
}
return false, nil
}
return true, nil
}
func ResourceCompute() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceComputeCreate,
ReadContext: resourceComputeRead,
UpdateContext: resourceComputeUpdate,
DeleteContext: resourceComputeDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout180s,
Read: &constants.Timeout30s,
Update: &constants.Timeout180s,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of this compute. Compute names are case sensitive and must be unique in the resource group.",
},
"rg_id": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntAtLeast(1),
Description: "ID of the resource group where this compute should be deployed.",
},
"driver": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
StateFunc: statefuncs.StateFuncToUpper,
ValidateFunc: validation.StringInSlice([]string{"KVM_X86", "KVM_PPC"}, false), // observe case while validating
Description: "Hardware architecture of this compute instance.",
},
"cpu": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(1, constants.MaxCpusPerCompute),
Description: "Number of CPUs to allocate to this compute instance.",
},
"ram": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntAtLeast(constants.MinRamPerCompute),
Description: "Amount of RAM in MB to allocate to this compute instance.",
},
"image_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
ValidateFunc: validation.IntAtLeast(1),
Description: "ID of the OS image to base this compute instance on.",
},
"boot_disk_size": {
Type: schema.TypeInt,
Required: true,
Description: "This compute instance boot disk size in GB. Make sure it is large enough to accomodate selected OS image.",
},
"sep_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
Description: "ID of SEP to create bootDisk on. Uses image's sepId if not set.",
},
"pool": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Description: "Pool to use if sepId is set, can be also empty if needed to be chosen by system.",
},
"extra_disks": {
Type: schema.TypeSet,
Optional: true,
MaxItems: constants.MaxExtraDisksPerCompute,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Description: "Optional list of IDs of extra disks to attach to this compute. You may specify several extra disks.",
},
"network": {
Type: schema.TypeSet,
Optional: true,
MaxItems: constants.MaxNetworksPerCompute,
Elem: &schema.Resource{
Schema: networkSubresourceSchemaMake(),
},
Description: "Optional network connection(s) for this compute. You may specify several network blocks, one for each connection.",
},
/*
"ssh_keys": {
Type: schema.TypeList,
Optional: true,
MaxItems: MaxSshKeysPerCompute,
Elem: &schema.Resource{
Schema: sshSubresourceSchemaMake(),
},
Description: "SSH keys to authorize on this compute instance.",
},
*/
"description": {
Type: schema.TypeString,
Optional: true,
Description: "Optional text description of this compute instance.",
},
"cloud_init": {
Type: schema.TypeString,
Optional: true,
Default: "applied",
DiffSuppressFunc: cloudInitDiffSupperss,
Description: "Optional cloud_init parameters. Applied when creating new compute instance only, ignored in all other cases.",
},
// The rest are Compute properties, which are "computed" once it is created
"rg_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the resource group where this compute instance is located.",
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the account this compute instance belongs to.",
},
"account_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the account this compute instance belongs to.",
},
"boot_disk_id": {
Type: schema.TypeInt,
Computed: true,
Description: "This compute instance boot disk ID.",
},
"os_users": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: osUsersSubresourceSchemaMake(),
},
Description: "Guest OS users provisioned on this compute instance.",
},
"started": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Is compute started.",
},
},
}
}

@ -0,0 +1,310 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package kvmvm
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityComputeExtraDisksConfigure(ctx context.Context, d *schema.ResourceData, m interface{}, do_delta bool) error {
// d is filled with data according to computeResource schema, so extra disks config is retrieved via "extra_disks" key
// If do_delta is true, this function will identify changes between new and existing specs for extra disks and try to
// update compute configuration accordingly
// Otherwise it will apply whatever is found in the new set of "extra_disks" right away.
// Primary use of do_delta=false is when calling this function from compute Create handler.
// Note that this function will not abort on API errors, but will continue to configure (attach / detach) other individual
// disks via atomic API calls. However, it will not retry failed manipulation on the same disk.
c := m.(*controller.ControllerCfg)
log.Debugf("utilityComputeExtraDisksConfigure: called for Compute ID %s with do_delta = %t", d.Id(), do_delta)
// NB: as of rc-1.25 "extra_disks" are TypeSet with the elem of TypeInt
old_set, new_set := d.GetChange("extra_disks")
apiErrCount := 0
var lastSavedError error
if !do_delta {
if new_set.(*schema.Set).Len() < 1 {
return nil
}
for _, disk := range new_set.(*schema.Set).List() {
urlValues := &url.Values{}
urlValues.Add("computeId", d.Id())
urlValues.Add("diskId", fmt.Sprintf("%d", disk.(int)))
_, err := c.DecortAPICall(ctx, "POST", ComputeDiskAttachAPI, urlValues)
if err != nil {
// failed to attach extra disk - partial resource update
apiErrCount++
lastSavedError = err
}
}
if apiErrCount > 0 {
log.Errorf("utilityComputeExtraDisksConfigure: there were %d error(s) when attaching disks to Compute ID %s. Last error was: %s",
apiErrCount, d.Id(), lastSavedError)
return lastSavedError
}
return nil
}
detach_set := old_set.(*schema.Set).Difference(new_set.(*schema.Set))
log.Debugf("utilityComputeExtraDisksConfigure: detach set has %d items for Compute ID %s", detach_set.Len(), d.Id())
for _, diskId := range detach_set.List() {
urlValues := &url.Values{}
urlValues.Add("computeId", d.Id())
urlValues.Add("diskId", fmt.Sprintf("%d", diskId.(int)))
_, err := c.DecortAPICall(ctx, "POST", ComputeDiskDetachAPI, urlValues)
if err != nil {
// failed to detach disk - there will be partial resource update
log.Errorf("utilityComputeExtraDisksConfigure: failed to detach disk ID %d from Compute ID %s: %s", diskId.(int), d.Id(), err)
apiErrCount++
lastSavedError = err
}
}
attach_set := new_set.(*schema.Set).Difference(old_set.(*schema.Set))
log.Debugf("utilityComputeExtraDisksConfigure: attach set has %d items for Compute ID %s", attach_set.Len(), d.Id())
for _, diskId := range attach_set.List() {
urlValues := &url.Values{}
urlValues.Add("computeId", d.Id())
urlValues.Add("diskId", fmt.Sprintf("%d", diskId.(int)))
_, err := c.DecortAPICall(ctx, "POST", ComputeDiskAttachAPI, urlValues)
if err != nil {
// failed to attach disk - there will be partial resource update
log.Errorf("utilityComputeExtraDisksConfigure: failed to attach disk ID %d to Compute ID %s: %s", diskId.(int), d.Id(), err)
apiErrCount++
lastSavedError = err
}
}
if apiErrCount > 0 {
log.Errorf("utilityComputeExtraDisksConfigure: there were %d error(s) when managing disks of Compute ID %s. Last error was: %s",
apiErrCount, d.Id(), lastSavedError)
return lastSavedError
}
return nil
}
func utilityComputeNetworksConfigure(ctx context.Context, d *schema.ResourceData, m interface{}, do_delta bool) error {
// "d" is filled with data according to computeResource schema, so extra networks config is retrieved via "network" key
// If do_delta is true, this function will identify changes between new and existing specs for network and try to
// update compute configuration accordingly
// Otherwise it will apply whatever is found in the new set of "network" right away.
// Primary use of do_delta=false is when calling this function from compute Create handler.
c := m.(*controller.ControllerCfg)
old_set, new_set := d.GetChange("network")
apiErrCount := 0
var lastSavedError error
if !do_delta {
if new_set.(*schema.Set).Len() < 1 {
return nil
}
for _, runner := range new_set.(*schema.Set).List() {
urlValues := &url.Values{}
net_data := runner.(map[string]interface{})
urlValues.Add("computeId", d.Id())
urlValues.Add("netType", net_data["net_type"].(string))
urlValues.Add("netId", fmt.Sprintf("%d", net_data["net_id"].(int)))
ipaddr, ipSet := net_data["ip_address"] // "ip_address" key is optional
if ipSet {
urlValues.Add("ipAddr", ipaddr.(string))
}
_, err := c.DecortAPICall(ctx, "POST", ComputeNetAttachAPI, urlValues)
if err != nil {
// failed to attach network - partial resource update
apiErrCount++
lastSavedError = err
}
}
if apiErrCount > 0 {
log.Errorf("utilityComputeNetworksConfigure: there were %d error(s) when managing networks of Compute ID %s. Last error was: %s",
apiErrCount, d.Id(), lastSavedError)
return lastSavedError
}
return nil
}
detach_set := old_set.(*schema.Set).Difference(new_set.(*schema.Set))
log.Debugf("utilityComputeNetworksConfigure: detach set has %d items for Compute ID %s", detach_set.Len(), d.Id())
for _, runner := range detach_set.List() {
urlValues := &url.Values{}
net_data := runner.(map[string]interface{})
urlValues.Add("computeId", d.Id())
urlValues.Add("ipAddr", net_data["ip_address"].(string))
urlValues.Add("mac", net_data["mac"].(string))
_, err := c.DecortAPICall(ctx, "POST", ComputeNetDetachAPI, urlValues)
if err != nil {
// failed to detach this network - there will be partial resource update
log.Errorf("utilityComputeNetworksConfigure: failed to detach net ID %d of type %s from Compute ID %s: %s",
net_data["net_id"].(int), net_data["net_type"].(string), d.Id(), err)
apiErrCount++
lastSavedError = err
}
}
attach_set := new_set.(*schema.Set).Difference(old_set.(*schema.Set))
log.Debugf("utilityComputeNetworksConfigure: attach set has %d items for Compute ID %s", attach_set.Len(), d.Id())
for _, runner := range attach_set.List() {
urlValues := &url.Values{}
net_data := runner.(map[string]interface{})
urlValues.Add("computeId", d.Id())
urlValues.Add("netId", fmt.Sprintf("%d", net_data["net_id"].(int)))
urlValues.Add("netType", net_data["net_type"].(string))
if net_data["ip_address"].(string) != "" {
urlValues.Add("ipAddr", net_data["ip_address"].(string))
}
_, err := c.DecortAPICall(ctx, "POST", ComputeNetAttachAPI, urlValues)
if err != nil {
// failed to attach this network - there will be partial resource update
log.Errorf("utilityComputeNetworksConfigure: failed to attach net ID %d of type %s to Compute ID %s: %s",
net_data["net_id"].(int), net_data["net_type"].(string), d.Id(), err)
apiErrCount++
lastSavedError = err
}
}
if apiErrCount > 0 {
log.Errorf("utilityComputeNetworksConfigure: there were %d error(s) when managing networks of Compute ID %s. Last error was: %s",
apiErrCount, d.Id(), lastSavedError)
return lastSavedError
}
return nil
}
func utilityComputeCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (string, error) {
// This function tries to locate Compute by one of the following approaches:
// - if compute_id is specified - locate by compute ID
// - if compute_name is specified - locate by a combination of compute name and resource
// group ID
//
// If succeeded, it returns non-empty string that contains JSON formatted facts about the
// Compute as returned by compute/get API call.
// Otherwise it returns empty string and meaningful error.
//
// This function does not modify its ResourceData argument, so it is safe to use it as core
// method for resource's Exists method.
//
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
// make it possible to use "read" & "check presence" functions with compute ID set so
// that Import of Compute resource is possible
idSet := false
theId, err := strconv.Atoi(d.Id())
if err != nil || theId <= 0 {
computeId, argSet := d.GetOk("compute_id") // NB: compute_id is NOT present in computeResource schema!
if argSet {
theId = computeId.(int)
idSet = true
}
} else {
idSet = true
}
if idSet {
// compute ID is specified, try to get compute instance straight by this ID
log.Debugf("utilityComputeCheckPresence: locating compute by its ID %d", theId)
urlValues.Add("computeId", fmt.Sprintf("%d", theId))
computeFacts, err := c.DecortAPICall(ctx, "POST", ComputeGetAPI, urlValues)
if err != nil {
return "", err
}
return computeFacts, nil
}
// ID was not set in the schema upon entering this function - work through Compute name
// and RG ID
computeName, argSet := d.GetOk("name")
if !argSet {
return "", fmt.Errorf("Cannot locate compute instance if name is empty and no compute ID specified")
}
rgId, argSet := d.GetOk("rg_id")
if !argSet {
return "", fmt.Errorf("Cannot locate compute by name %s if no resource group ID is set", computeName.(string))
}
urlValues.Add("rgId", fmt.Sprintf("%d", rgId))
apiResp, err := c.DecortAPICall(ctx, "POST", RgListComputesAPI, urlValues)
if err != nil {
return "", err
}
log.Debugf("utilityComputeCheckPresence: ready to unmarshal string %s", apiResp)
computeList := RgListComputesResp{}
err = json.Unmarshal([]byte(apiResp), &computeList)
if err != nil {
return "", err
}
// log.Printf("%#v", computeList)
log.Debugf("utilityComputeCheckPresence: traversing decoded JSON of length %d", len(computeList))
for index, item := range computeList {
// need to match Compute by name, skip Computes with the same name in DESTROYED satus
if item.Name == computeName.(string) && item.Status != "DESTROYED" {
log.Debugf("utilityComputeCheckPresence: index %d, matched name %s", index, item.Name)
// we found the Compute we need - now get detailed information via compute/get API
cgetValues := &url.Values{}
cgetValues.Add("computeId", fmt.Sprintf("%d", item.ID))
apiResp, err = c.DecortAPICall(ctx, "POST", ComputeGetAPI, cgetValues)
if err != nil {
return "", err
}
return apiResp, nil
}
}
return "", nil // there should be no error if Compute does not exist
}

@ -47,22 +47,6 @@ import (
func resourcePcideviceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourcePcideviceCreate: called for pcidevice %s", d.Get("name").(string))
if deviceId, ok := d.GetOk("device_id"); ok {
if exists, err := resourcePcideviceExists(ctx, d, m); exists {
if err != nil {
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(deviceId.(int)))
diagnostics := resourcePcideviceRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
return nil
}
return diag.Errorf("provided device id does not exist")
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("name", d.Get("name").(string))

@ -0,0 +1,36 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package pfw
const ComputePfwListAPI = "/restmachine/cloudbroker/compute/pfwList"
const ComputePfwAddAPI = "/restmachine/cloudbroker/compute/pfwAdd"
const ComputePfwDelAPI = "/restmachine/cloudbroker/compute/pfwDel"

@ -0,0 +1,45 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package pfw
type PfwRecord struct {
ID int `json:"id"`
LocalIP string `json:"localIp"`
LocalPort int `json:"localPort"`
Protocol string `json:"protocol"`
PublicPortEnd int `json:"publicPortEnd"`
PublicPortStart int `json:"publicPortStart"`
ComputeID int `json:"vmId"`
}
type ComputePfwListResp []PfwRecord

@ -0,0 +1,211 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package pfw
import (
"context"
"fmt"
"net/url"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
)
func resourcePfwCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourcePfwCreate: called for compute %d", d.Get("compute_id").(int))
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
urlValues.Add("publicPortStart", strconv.Itoa(d.Get("public_port_start").(int)))
urlValues.Add("localBasePort", strconv.Itoa(d.Get("local_base_port").(int)))
urlValues.Add("proto", d.Get("proto").(string))
if portEnd, ok := d.GetOk("public_port_end"); ok {
urlValues.Add("publicPortEnd", strconv.Itoa(portEnd.(int)))
}
pfwId, err := c.DecortAPICall(ctx, "POST", ComputePfwAddAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId(fmt.Sprintf("%d-%s", d.Get("compute_id").(int), pfwId))
pfw, err := utilityPfwCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
d.Set("local_ip", pfw.LocalIP)
if _, ok := d.GetOk("public_port_end"); !ok {
d.Set("public_port_end", pfw.PublicPortEnd)
}
return nil
}
func resourcePfwRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourcePfwRead: called for compute %d, rule %s", d.Get("compute_id").(int), d.Id())
pfw, err := utilityPfwCheckPresence(ctx, d, m)
if pfw == nil {
d.SetId("")
return diag.FromErr(err)
}
d.Set("compute_id", pfw.ComputeID)
d.Set("public_port_start", pfw.PublicPortStart)
d.Set("public_port_end", pfw.PublicPortEnd)
d.Set("local_ip", pfw.LocalIP)
d.Set("local_base_port", pfw.LocalPort)
d.Set("proto", pfw.Protocol)
return nil
}
func resourcePfwDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourcePfwDelete: called for compute %d, rule %s", d.Get("compute_id").(int), d.Id())
pfw, err := utilityPfwCheckPresence(ctx, d, m)
if pfw == nil {
if err != nil {
return diag.FromErr(err)
}
return nil
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
urlValues.Add("ruleId", strconv.Itoa(pfw.ID))
_, err = c.DecortAPICall(ctx, "POST", ComputePfwDelAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
return nil
}
func resourcePfwExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
log.Debugf("resourcePfwExists: called for compute %d, rule %s", d.Get("compute_id").(int), d.Id())
pfw, err := utilityPfwCheckPresence(ctx, d, m)
if pfw == nil {
if err != nil {
return false, err
}
return false, nil
}
return true, nil
}
func resourcePfwSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"compute_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "ID of compute instance.",
},
"public_port_start": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
ValidateFunc: validation.IntBetween(1, 65535),
Description: "External start port number for the rule.",
},
"public_port_end": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validation.IntBetween(1, 65535),
Description: "End port number (inclusive) for the ranged rule.",
},
"local_ip": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of compute instance.",
},
"local_base_port": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
ValidateFunc: validation.IntBetween(1, 65535),
Description: "Internal base port number.",
},
"proto": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false),
Description: "Network protocol, either 'tcp' or 'udp'.",
},
}
}
func ResourcePfw() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourcePfwCreate,
ReadContext: resourcePfwRead,
DeleteContext: resourcePfwDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout60s,
Read: &constants.Timeout30s,
Update: &constants.Timeout60s,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: resourcePfwSchemaMake(),
}
}

@ -0,0 +1,77 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package pfw
import (
"context"
"encoding/json"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/controller"
)
func utilityPfwCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*PfwRecord, error) {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
resp, err := c.DecortAPICall(ctx, "POST", ComputePfwListAPI, urlValues)
if err != nil {
return nil, err
}
if resp == "" {
return nil, nil
}
idS := strings.Split(d.Id(), "-")[1]
id, err := strconv.Atoi(idS)
if err != nil {
return nil, err
}
var pfws []PfwRecord
if err := json.Unmarshal([]byte(resp), &pfws); err != nil {
return nil, err
}
for _, pfw := range pfws {
if pfw.ID == id {
return &pfw, nil
}
}
return nil, nil
}

@ -0,0 +1,39 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package rg
const ResgroupCreateAPI = "/restmachine/cloudbroker/rg/create"
const ResgroupUpdateAPI = "/restmachine/cloudbroker/rg/update"
const ResgroupListAPI = "/restmachine/cloudbroker/rg/list"
const ResgroupGetAPI = "/restmachine/cloudbroker/rg/get"
const ResgroupDeleteAPI = "/restmachine/cloudbroker/rg/delete"
const RgListComputesAPI = "/restmachine/cloudbroker/rg/listComputes"

@ -0,0 +1,196 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package rg
import (
"context"
"encoding/json"
"fmt"
"github.com/rudecs/terraform-provider-decort/internal/constants"
log "github.com/sirupsen/logrus"
// "net/url"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
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 API")
details := ResgroupGetResp{}
err := json.Unmarshal([]byte(rg_facts), &details)
if err != nil {
return err
}
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("description", details.Desc)
d.Set("status", details.Status)
d.Set("def_net_type", details.DefaultNetType)
d.Set("def_net_id", details.DefaultNetID)
/*
d.Set("vins", details.Vins)
d.Set("computes", details.Computes)
*/
log.Debugf("flattenResgroup: calling flattenQuota()")
if err = d.Set("quota", parseQuota(details.Quota)); err != nil {
return err
}
return nil
}
func dataSourceResgroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
rg_facts, err := utilityResgroupCheckPresence(ctx, d, m)
if rg_facts == "" {
// if empty string is returned from utilityResgroupCheckPresence then there is no
// such resource group and err tells so - just return it to the calling party
d.SetId("") // ensure ID is empty in this case
return diag.FromErr(err)
}
return diag.FromErr(flattenResgroup(d, rg_facts))
}
func DataSourceResgroup() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceResgroupRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of the resource group. Names are case sensitive and unique within the context of an account.",
},
"rg_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Unique ID of the resource group. If this ID is specified, then resource group name is ignored.",
},
"account_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the account, which this resource group belongs to.",
},
"account_id": {
Type: schema.TypeInt,
Required: true,
Description: "Unique ID of the account, which this resource group belongs to.",
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: "User-defined text description of this resource group.",
},
/* commented out, as in this version of provider we use default Grid ID
"grid_id": {
Type: schema.TypeInt,
Computed: true,
Description: "Unique ID of the grid, where this resource group is deployed.",
},
*/
"quota": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: quotaRgSubresourceSchemaMake(), // this is a dictionary
},
Description: "Quota settings for this resource group.",
},
"def_net_type": {
Type: schema.TypeString,
Computed: true,
Description: "Type of the default network for this resource group.",
},
"def_net_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the default network for this resource group (if any).",
},
/*
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current status of this resource group.",
},
"vins": {
Type: schema.TypeList, // this is a list of ints
Computed: true,
MaxItems: LimitMaxVinsPerResgroup,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Description: "List of VINs deployed in this resource group.",
},
"computes": {
Type: schema.TypeList, //t his is a list of ints
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Description: "List of computes deployed in this resource group.",
},
*/
},
}
}

@ -0,0 +1,324 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package rg
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenRgList(rgl ResgroupListResp) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, rg := range rgl {
temp := map[string]interface{}{
"account_id": rg.AccountID,
"account_name": rg.AccountName,
"acl": flattenRgAcl(rg.ACLs),
"created_by": rg.CreatedBy,
"created_time": rg.CreatedTime,
"def_net_id": rg.DefaultNetID,
"def_net_type": rg.DefaultNetType,
"deleted_by": rg.DeletedBy,
"deleted_time": rg.DeletedTime,
"desc": rg.Decsription,
"gid": rg.GridID,
"guid": rg.GUID,
"rg_id": rg.ID,
"lock_status": rg.LockStatus,
"milestones": rg.Milestones,
"name": rg.Name,
"register_computes": rg.RegisterComputes,
"resource_limits": flattenRgResourceLimits(rg.ResourceLimits),
"secret": rg.Secret,
"status": rg.Status,
"updated_by": rg.UpdatedBy,
"updated_time": rg.UpdatedTime,
"vins": rg.Vins,
"vms": rg.Computes,
}
res = append(res, temp)
}
return res
}
func flattenRgAcl(rgAcls []AccountAclRecord) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, rgAcl := range rgAcls {
temp := map[string]interface{}{
"explicit": rgAcl.IsExplicit,
"guid": rgAcl.Guid,
"right": rgAcl.Rights,
"status": rgAcl.Status,
"type": rgAcl.Type,
"user_group_id": rgAcl.UgroupID,
}
res = append(res, temp)
}
return res
}
func flattenRgResourceLimits(rl ResourceLimits) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"cu_c": rl.CUC,
"cu_d": rl.CUD,
"cu_i": rl.CUI,
"cu_m": rl.CUM,
"cu_np": rl.CUNP,
"gpu_units": rl.GpuUnits,
}
res = append(res, temp)
return res
}
func dataSourceRgListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
rgList, err := utilityRgListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenRgList(rgList))
return nil
}
func dataSourceRgListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"includedeleted": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "included deleted resource groups",
},
"page": {
Type: schema.TypeInt,
Optional: true,
Description: "Page number",
},
"size": {
Type: schema.TypeInt,
Optional: true,
Description: "Page size",
},
"items": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"explicit": {
Type: schema.TypeBool,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"right": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"user_group_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"def_net_id": {
Type: schema.TypeInt,
Computed: true,
},
"def_net_type": {
Type: schema.TypeString,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Computed: true,
},
"gid": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"rg_id": {
Type: schema.TypeInt,
Computed: true,
},
"lock_status": {
Type: schema.TypeString,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"register_computes": {
Type: schema.TypeBool,
Computed: true,
},
"resource_limits": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cu_c": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_d": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_i": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_m": {
Type: schema.TypeFloat,
Computed: true,
},
"cu_np": {
Type: schema.TypeFloat,
Computed: true,
},
"gpu_units": {
Type: schema.TypeFloat,
Computed: true,
},
},
},
},
"secret": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"vins": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"vms": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
},
},
},
}
return res
}
func DataSourceRgList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceRgListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceRgListSchemaMake(),
}
}

@ -0,0 +1,149 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package rg
type ResourceLimits struct {
CUC float64 `json:"CU_C"`
CUD float64 `json:"CU_D"`
CUI float64 `json:"CU_I"`
CUM float64 `json:"CU_M"`
CUNP float64 `json:"CU_NP"`
GpuUnits float64 `json:"gpu_units"`
}
type ResgroupRecord struct {
ACLs []AccountAclRecord `json:"acl"`
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
CreatedBy string `json:"createdBy"`
CreatedTime uint64 `json:"createdTime"`
DefaultNetID int `json:"def_net_id"`
DefaultNetType string `json:"def_net_type"`
DeletedBy string `json:"deletedBy"`
DeletedTime int `json:"deletedTime"`
Decsription string `json:"desc"`
GridID int `json:"gid"`
GUID int `json:"guid"`
ID uint `json:"id"`
LockStatus string `json:"lockStatus"`
Milestones int `json:"milestones"`
Name string `json:"name"`
RegisterComputes bool `json:"registerComputes"`
ResourceLimits ResourceLimits `json:"resourceLimits"`
Secret string `json:"secret"`
Status string `json:"status"`
UpdatedBy string `json:"updatedBy"`
UpdatedTime uint64 `json:"updatedTime"`
Vins []int `json:"vins"`
Computes []int `json:"vms"`
}
type ResgroupListResp []ResgroupRecord
type ResgroupUpdateParam struct {
RgId int `json:"rgId"`
Name string `json:"name"`
Desc string `json:"decs"`
Ram int `json:"maxMemoryCapacity"`
Disk int `json:"maxVDiskCapacity"`
Cpu int `json:"maxCPUCapacity"`
NetTraffic int `json:"maxNetworkPeerTransfer"`
Reason string `json:"reason"`
}
type AccountAclRecord struct {
IsExplicit bool `json:"explicit"`
Guid string `json:"guid"`
Rights string `json:"right"`
Status string `json:"status"`
Type string `json:"type"`
UgroupID string `json:"userGroupId"`
CanBeDeleted bool `json:"canBeDeleted"`
}
type ResgroupGetResp struct {
ACLs []UserAclRecord `json:"ACLs"`
Usage UsageRecord `json:"Resources"`
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
GridID int `json:"gid"`
CreatedBy string `json:"createdBy"`
CreatedTime uint64 `json:"createdTime"`
DefaultNetID int `json:"def_net_id"`
DefaultNetType string `json:"def_net_type"`
DeletedBy string `json:"deletedBy"`
DeletedTime uint64 `json:"deletedTime"`
Desc string `json:"desc"`
ID uint `json:"id"`
LockStatus string `json:"lockStatus"`
Name string `json:"name"`
Quota QuotaRecord `json:"resourceLimits"`
Status string `json:"status"`
UpdatedBy string `json:"updatedBy"`
UpdatedTime uint64 `json:"updatedTime"`
Vins []int `json:"vins"`
Computes []int `json:"vms"`
Ignored map[string]interface{} `json:"-"`
}
type UserAclRecord struct {
IsExplicit bool `json:"explicit"`
Rights string `json:"right"`
Status string `json:"status"`
Type string `json:"type"`
UgroupID string `json:"userGroupId"`
// CanBeDeleted bool `json:"canBeDeleted"`
}
type QuotaRecord struct { // this is how quota is reported by /api/.../rg/get
Cpu int `json:"CU_C"` // CPU count in pcs
Ram float64 `json:"CU_M"` // RAM volume in MB, it is STILL reported as FLOAT
Disk int `json:"CU_D"` // Disk capacity in GB
ExtIPs int `json:"CU_I"` // Ext IPs count
ExtTraffic int `json:"CU_NP"` // Ext network traffic
GpuUnits int `json:"gpu_units"` // GPU count
}
type ResourceRecord struct { // this is how actual usage is reported by /api/.../rg/get
Cpu int `json:"cpu"`
Disk int `json:"disksize"`
ExtIPs int `json:"extips"`
ExtTraffic int `json:"exttraffic"`
Gpu int `json:"gpu"`
Ram int `json:"ram"`
}
type UsageRecord struct {
Current ResourceRecord `json:"Current"`
Reserved ResourceRecord `json:"Reserved"`
}

@ -0,0 +1,137 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package rg
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func makeQuotaRecord(arg_list []interface{}) QuotaRecord {
quota := QuotaRecord{
Cpu: -1,
Ram: -1., // this is float64, but may change in the future
Disk: -1,
ExtTraffic: -1,
ExtIPs: -1,
GpuUnits: -1,
}
subres_data := arg_list[0].(map[string]interface{})
if subres_data["cpu"].(int) > 0 {
quota.Cpu = subres_data["cpu"].(int)
}
if subres_data["disk"].(int) > 0 {
quota.Disk = subres_data["disk"].(int) // Disk capacity ib GB
}
if subres_data["ram"].(float64) > 0 {
quota.Ram = subres_data["ram"].(float64) // RAM volume in MB, as float64!
}
if subres_data["ext_traffic"].(int) > 0 {
quota.ExtTraffic = subres_data["ext_traffic"].(int)
}
if subres_data["ext_ips"].(int) > 0 {
quota.ExtIPs = subres_data["ext_ips"].(int)
}
if subres_data["gpu_units"].(int) > 0 {
quota.GpuUnits = subres_data["gpu_units"].(int)
}
return quota
}
func parseQuota(quota QuotaRecord) []interface{} {
quota_map := make(map[string]interface{})
quota_map["cpu"] = quota.Cpu
quota_map["ram"] = quota.Ram // NB: this is float64, unlike the rest of values
quota_map["disk"] = quota.Disk
quota_map["ext_traffic"] = quota.ExtTraffic
quota_map["ext_ips"] = quota.ExtIPs
quota_map["gpu_units"] = quota.GpuUnits
result := make([]interface{}, 1)
result[0] = quota_map
return result // this result will be used to d.Set("quota,") of dataSourceResgroup schema
}
func quotaRgSubresourceSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Optional: true,
Default: -1,
Description: "Limit on the total number of CPUs in this resource group.",
},
"ram": {
Type: schema.TypeFloat, // NB: API expects and returns this as float in units of MB! This may be changed in the future.
Optional: true,
Default: -1.,
Description: "Limit on the total amount of RAM in this resource group, specified in MB.",
},
"disk": {
Type: schema.TypeInt,
Optional: true,
Default: -1,
Description: "Limit on the total volume of storage resources in this resource group, specified in GB.",
},
"ext_traffic": {
Type: schema.TypeInt,
Optional: true,
Default: -1,
Description: "Limit on the total ingress network traffic for this resource group, specified in GB.",
},
"ext_ips": {
Type: schema.TypeInt,
Optional: true,
Default: -1,
Description: "Limit on the total number of external IP addresses this resource group can use.",
},
"gpu_units": {
Type: schema.TypeInt,
Optional: true,
Default: -1,
Description: "Limit on the total number of virtual GPUs this resource group can use.",
},
}
return rets
}

@ -0,0 +1,442 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package rg
import (
"context"
"encoding/json"
"fmt"
"net/url"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
"github.com/rudecs/terraform-provider-decort/internal/location"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func resourceResgroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// First validate that we have all parameters required to create the new Resource Group
// Valid account ID is required to create new resource group
// obtain Account ID by account name - it should not be zero on success
rg_name, arg_set := d.GetOk("name")
if !arg_set {
return diag.FromErr(fmt.Errorf("Cannot create new RG: missing name."))
}
/* Current version of provider works with default grid id (same is true for disk resources)
grid_id, arg_set := d.GetOk("grid_id")
if !arg_set {
return fmt.Errorf("Cannot create new RG %q in account ID %d: missing Grid ID.",
rg_name.(string), validated_account_id)
}
if grid_id.(int) < 1 {
grid_id = DefaultGridID
}
*/
// all required parameters are set in the schema - we can continue with RG creation
log.Debugf("resourceResgroupCreate: called for RG name %s, account ID %d",
rg_name.(string), d.Get("account_id").(int))
// quota settings are optional
set_quota := false
var quota_record QuotaRecord
arg_value, arg_set := d.GetOk("quota")
if arg_set {
log.Debugf("resourceResgroupCreate: setting Quota on RG requested")
quota_record = makeQuotaRecord(arg_value.([]interface{}))
set_quota = true
}
c := m.(*controller.ControllerCfg)
log.Debugf("resourceResgroupCreate: called by user %q for RG name %s, account ID %d",
c.GetDecortUsername(),
rg_name.(string), d.Get("account_id").(int))
url_values := &url.Values{}
url_values.Add("accountId", fmt.Sprintf("%d", d.Get("account_id").(int)))
url_values.Add("name", rg_name.(string))
url_values.Add("gid", fmt.Sprintf("%d", location.DefaultGridID)) // use default Grid ID, similar to disk resource mgmt convention
url_values.Add("owner", c.GetDecortUsername())
// pass quota values as set
if set_quota {
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quota_record.Cpu))
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quota_record.Disk))
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quota_record.Ram)) // RAM quota is float; this may change in the future
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quota_record.ExtTraffic))
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quota_record.ExtIPs))
// url_values.Add("???", fmt.Sprintf("%d", quota_record.GpuUnits))
}
// parse and handle network settings
def_net_type, arg_set := d.GetOk("def_net_type")
if arg_set {
url_values.Add("def_net", def_net_type.(string)) // NOTE: in API default network type is set by "def_net" parameter
}
ipcidr, arg_set := d.GetOk("ipcidr")
if arg_set {
url_values.Add("ipcidr", ipcidr.(string))
}
ext_net_id, arg_set := d.GetOk("ext_net_id")
if arg_set {
url_values.Add("extNetId", fmt.Sprintf("%d", ext_net_id.(int)))
}
ext_ip, arg_set := d.GetOk("ext_ip")
if arg_set {
url_values.Add("extIp", ext_ip.(string))
}
api_resp, err := c.DecortAPICall(ctx, "POST", ResgroupCreateAPI, url_values)
if err != nil {
return diag.FromErr(err)
}
d.SetId(api_resp) // rg/create API returns ID of the newly creted resource group on success
// rg.ID, _ = strconv.Atoi(api_resp)
if !set_quota {
resp, err := utilityResgroupCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
rg := ResgroupGetResp{}
if err := json.Unmarshal([]byte(resp), &rg); err != nil {
return diag.FromErr(err)
}
d.Set("quota", parseQuota(rg.Quota))
}
// re-read newly created RG to make sure schema contains complete and up to date set of specifications
return resourceResgroupRead(ctx, d, m)
}
func resourceResgroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceResgroupRead: called for RG name %s, account ID %d",
d.Get("name").(string), d.Get("account_id").(int))
rg_facts, err := utilityResgroupCheckPresence(ctx, d, m)
if rg_facts == "" {
// if empty string is returned from utilityResgroupCheckPresence then there is no
// such resource group and err tells so - just return it to the calling party
d.SetId("") // ensure ID is empty
return diag.FromErr(err)
}
return diag.FromErr(flattenResgroup(d, rg_facts))
}
func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceResgroupUpdate: called for RG name %s, account ID %d",
d.Get("name").(string), d.Get("account_id").(int))
/* NOTE: we do not allow changing the following attributes of an existing RG via terraform:
- def_net_type
- ipcidr
- ext_net_id
- ext_ip
The following code fragment checks if any of these have been changed and generates error.
*/
for _, attr := range []string{"def_net_type", "ipcidr", "ext_ip"} {
attr_new, attr_old := d.GetChange("def_net_type")
if attr_new.(string) != attr_old.(string) {
return diag.FromErr(fmt.Errorf("resourceResgroupUpdate: RG ID %s: changing %s for existing RG is not allowed", d.Id(), attr))
}
}
attr_new, attr_old := d.GetChange("ext_net_id")
if attr_new.(int) != attr_old.(int) {
return diag.FromErr(fmt.Errorf("resourceResgroupUpdate: RG ID %s: changing ext_net_id for existing RG is not allowed", d.Id()))
}
do_general_update := false // will be true if general RG update is necessary (API rg/update)
c := m.(*controller.ControllerCfg)
url_values := &url.Values{}
url_values.Add("rgId", d.Id())
name_new, name_set := d.GetOk("name")
if name_set {
log.Debugf("resourceResgroupUpdate: name specified - looking for deltas from the old settings.")
name_old, _ := d.GetChange("name")
if name_old.(string) != name_new.(string) {
do_general_update = true
url_values.Add("name", name_new.(string))
}
}
quota_value, quota_set := d.GetOk("quota")
if quota_set {
log.Debugf("resourceResgroupUpdate: quota specified - looking for deltas from the old quota.")
quotarecord_new := makeQuotaRecord(quota_value.([]interface{}))
quota_value_old, _ := d.GetChange("quota") // returns old as 1st, new as 2nd return value
quotarecord_old := makeQuotaRecord(quota_value_old.([]interface{}))
if quotarecord_new.Cpu != quotarecord_old.Cpu {
do_general_update = true
log.Debugf("resourceResgroupUpdate: Cpu diff %d <- %d", quotarecord_new.Cpu, quotarecord_old.Cpu)
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quotarecord_new.Cpu))
}
if quotarecord_new.Disk != quotarecord_old.Disk {
do_general_update = true
log.Debugf("resourceResgroupUpdate: Disk diff %d <- %d", quotarecord_new.Disk, quotarecord_old.Disk)
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotarecord_new.Disk))
}
if quotarecord_new.Ram != quotarecord_old.Ram { // NB: quota on RAM is stored as float32, in units of MB
do_general_update = true
log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotarecord_new.Ram, quotarecord_old.Ram)
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotarecord_new.Ram))
}
if quotarecord_new.ExtTraffic != quotarecord_old.ExtTraffic {
do_general_update = true
log.Debugf("resourceResgroupUpdate: ExtTraffic diff %d <- %d", quotarecord_new.ExtTraffic, quotarecord_old.ExtTraffic)
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quotarecord_new.ExtTraffic))
}
if quotarecord_new.ExtIPs != quotarecord_old.ExtIPs {
do_general_update = true
log.Debugf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotarecord_new.ExtIPs, quotarecord_old.ExtIPs)
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quotarecord_new.ExtIPs))
}
}
desc_new, desc_set := d.GetOk("description")
if desc_set {
log.Debugf("resourceResgroupUpdate: description specified - looking for deltas from the old settings.")
desc_old, _ := d.GetChange("description")
if desc_old.(string) != desc_new.(string) {
do_general_update = true
url_values.Add("desc", desc_new.(string))
}
}
if do_general_update {
log.Debugf("resourceResgroupUpdate: detected delta between new and old RG specs - updating the RG")
_, err := c.DecortAPICall(ctx, "POST", ResgroupUpdateAPI, url_values)
if err != nil {
return diag.FromErr(err)
}
} else {
log.Debugf("resourceResgroupUpdate: no difference between old and new state - no update on the RG will be done")
}
return resourceResgroupRead(ctx, d, m)
}
func resourceResgroupDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// 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 %s, account ID %d",
d.Get("name").(string), d.Get("account_id").(int))
rg_facts, err := utilityResgroupCheckPresence(ctx, d, m)
if rg_facts == "" {
if err != nil {
return diag.FromErr(err)
}
// the target RG does not exist - in this case according to Terraform best practice
// we exit from Destroy method without error
return nil
}
url_values := &url.Values{}
url_values.Add("rgId", d.Id())
url_values.Add("force", "1")
url_values.Add("permanently", "1")
url_values.Add("reason", "Destroyed by DECORT Terraform provider")
c := m.(*controller.ControllerCfg)
_, err = c.DecortAPICall(ctx, "POST", ResgroupDeleteAPI, url_values)
if err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceResgroupExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
// Reminder: according to Terraform rules, this function should NOT modify ResourceData argument
rg_facts, err := utilityResgroupCheckPresence(ctx, d, m)
if rg_facts == "" {
if err != nil {
return false, err
}
return false, nil
}
return true, nil
}
func ResourceResgroup() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceResgroupCreate,
ReadContext: resourceResgroupRead,
UpdateContext: resourceResgroupUpdate,
DeleteContext: resourceResgroupDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout180s,
Read: &constants.Timeout30s,
Update: &constants.Timeout180s,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
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 a account.",
},
"account_id": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntAtLeast(1),
Description: "Unique ID of the account, which this resource group belongs to.",
},
"def_net_type": {
Type: schema.TypeString,
Optional: true,
Default: "PRIVATE",
ValidateFunc: validation.StringInSlice([]string{"PRIVATE", "PUBLIC", "NONE"}, false),
Description: "Type of the network, which this resource group will use as default for its computes - PRIVATE or PUBLIC or NONE.",
},
"def_net_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the default network for this resource group (if any).",
},
"ipcidr": {
Type: schema.TypeString,
Optional: true,
Description: "Address of the netowrk inside the private network segment (aka ViNS) if def_net_type=PRIVATE",
},
"ext_net_id": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
Description: "ID of the external network for default ViNS. Pass 0 if def_net_type=PUBLIC or no external connection required for the defult ViNS when def_net_type=PRIVATE",
},
"ext_ip": {
Type: schema.TypeString,
Optional: true,
Description: "IP address on the external netowrk to request when def_net_type=PRIVATE and ext_net_id is not 0",
},
/* commented out, as in this version of provider we use default Grid ID
"grid_id": {
Type: schema.TypeInt,
Optional: true,
Default: 0, // if 0 is passed, default Grid ID will be used
// DefaultFunc: utilityResgroupGetDefaultGridID,
ForceNew: true, // change of Grid ID will require new RG
Description: "Unique ID of the grid, where this resource group is deployed.",
},
*/
"quota": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: quotaRgSubresourceSchemaMake(),
},
Description: "Quota settings for this resource group.",
},
"description": {
Type: schema.TypeString,
Optional: true,
Description: "User-defined text description of this resource group.",
},
"account_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the account, which this resource group belongs to.",
},
/*
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current status of this resource group.",
},
"vins": {
Type: schema.TypeList, // this is a list of ints
Computed: true,
MaxItems: LimitMaxVinsPerResgroup,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Description: "List of VINs deployed in this resource group.",
},
"computes": {
Type: schema.TypeList, // this is a list of ints
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Description: "List of computes deployed in this resource group.",
},
*/
},
}
}

@ -0,0 +1,139 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package rg
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
// On success this function returns a string, as returned by API rg/get, which could be unmarshalled
// into ResgroupGetResp structure
func utilityResgroupCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (string, error) {
// 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 rg/get API call.
// Otherwise it returns empty string and a meaningful error.
//
// NOTE: As our provider always deletes RGs permanently, there is no "restore" method and
// consequently we are not interested in matching RGs in DELETED state. Hence, we call
// .../rg/list API with includedeleted=false
//
// This function does not modify its ResourceData argument, so it is safe to use it as core
// method for the Terraform resource Exists method.
//
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
// make it possible to use "read" & "check presence" functions with RG ID set so
// that Import of RG resource is possible
idSet := false
theId, err := strconv.Atoi(d.Id())
if err != nil || theId <= 0 {
rgId, argSet := d.GetOk("rg_id")
if argSet {
theId = rgId.(int)
idSet = true
}
} else {
idSet = true
}
if idSet {
// go straight for the RG by its ID
log.Debugf("utilityResgroupCheckPresence: locating RG by its ID %d", theId)
urlValues.Add("rgId", fmt.Sprintf("%d", theId))
rgFacts, err := c.DecortAPICall(ctx, "POST", ResgroupGetAPI, urlValues)
if err != nil {
return "", err
}
return rgFacts, nil
}
rgName, argSet := d.GetOk("name")
if !argSet {
// no RG ID and no RG name - we cannot locate resource group in this case
return "", fmt.Errorf("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
urlValues.Add("includedeleted", "false")
apiResp, err := c.DecortAPICall(ctx, "POST", ResgroupListAPI, urlValues)
if err != nil {
return "", err
}
// log.Debugf("%s", apiResp)
log.Debugf("utilityResgroupCheckPresence: ready to decode response body from %s", ResgroupListAPI)
model := ResgroupListResp{}
err = json.Unmarshal([]byte(apiResp), &model)
if err != nil {
return "", err
}
log.Debugf("utilityResgroupCheckPresence: traversing decoded Json of length %d", len(model))
for index, item := range model {
// match by RG name & account ID
if item.Name == rgName.(string) && item.AccountID == d.Get("account_id").(int) {
log.Debugf("utilityResgroupCheckPresence: match RG name %s / 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 resource quota settings
reqValues := &url.Values{}
reqValues.Add("rgId", fmt.Sprintf("%d", item.ID))
apiResp, err := c.DecortAPICall(ctx, "POST", ResgroupGetAPI, reqValues)
if err != nil {
return "", err
}
return apiResp, nil
}
}
return "", fmt.Errorf("Cannot find RG name %s owned by account ID %d", rgName, d.Get("account_id").(int))
}

@ -0,0 +1,74 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package rg
import (
"context"
"encoding/json"
"net/url"
"strconv"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityRgListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (ResgroupListResp, error) {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
rgList := ResgroupListResp{}
if size, ok := d.GetOk("size"); ok {
urlValues.Add("size", strconv.Itoa(size.(int)))
}
if page, ok := d.GetOk("page"); ok {
urlValues.Add("page", strconv.Itoa(page.(int)))
}
if includedeleted, ok := d.GetOk("includedeleted"); ok {
urlValues.Add("includedeleted", strconv.FormatBool(includedeleted.(bool)))
}
log.Debugf("utilityRgListCheckPresence: load rg list")
rgListRaw, err := c.DecortAPICall(ctx, "POST", ResgroupListAPI, urlValues)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(rgListRaw), &rgList)
if err != nil {
return nil, err
}
return rgList, nil
}

@ -38,7 +38,6 @@ import (
"net/url"
"strconv"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
@ -50,22 +49,6 @@ import (
func resourceSepCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceSepCreate: called for sep %s", d.Get("name").(string))
if sepId, ok := d.GetOk("sep_id"); ok {
if exists, err := resourceSepExists(ctx, d, m); exists {
if err != nil {
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(sepId.(int)))
diagnostics := resourceSepRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
return nil
}
return diag.Errorf("provided sep id does not exist")
}
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
@ -114,7 +97,6 @@ func resourceSepCreate(ctx context.Context, d *schema.ResourceData, m interface{
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(sepId)
d.Set("sep_id", sepId)
@ -123,8 +105,6 @@ func resourceSepCreate(ctx context.Context, d *schema.ResourceData, m interface{
return diagnostics
}
d.SetId(id.String())
return nil
}

@ -0,0 +1,37 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package snapshot
const snapshotCreateAPI = "/restmachine/cloudbroker/compute/snapshotCreate"
const snapshotDeleteAPI = "/restmachine/cloudbroker/compute/snapshotDelete"
const snapshotRollbackAPI = "/restmachine/cloudbroker/compute/snapshotRollback"
const snapshotListAPI = "/restmachine/cloudbroker/compute/snapshotList"

@ -0,0 +1,131 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package snapshot
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
)
func flattenSnapshotList(gl SnapshotList) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, item := range gl {
temp := map[string]interface{}{
"label": item.Label,
"guid": item.Guid,
"disks": item.Disks,
"timestamp": item.Timestamp,
}
res = append(res, temp)
}
return res
}
func dataSourceSnapshotListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
snapshotList, err := utilitySnapshotListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenSnapshotList(snapshotList))
return nil
}
func dataSourceSnapshotListSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"compute_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "ID of the compute instance to create snapshot for.",
},
"items": {
Type: schema.TypeList,
Computed: true,
Description: "snapshot list",
Elem: &schema.Resource{
Schema: dataSourceSnapshotSchemaMake(),
},
},
}
return rets
}
func dataSourceSnapshotSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"label": {
Type: schema.TypeString,
Computed: true,
Description: "text label for snapshot. Must be unique among this compute snapshots.",
},
"disks": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"guid": {
Type: schema.TypeString,
Computed: true,
Description: "guid of the snapshot",
},
"timestamp": {
Type: schema.TypeInt,
Computed: true,
Description: "timestamp",
},
}
}
func DataSourceSnapshotList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceSnapshotListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceSnapshotListSchemaMake(),
}
}

@ -0,0 +1,41 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package snapshot
type Snapshot struct {
Disks []int `json:"disks"`
Guid string `json:"guid"`
Label string `json:"label"`
Timestamp uint64 `json:"timestamp"`
}
type SnapshotList []Snapshot

@ -0,0 +1,206 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://github.com/rudecs/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
*/
package snapshot
import (
"context"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/rudecs/terraform-provider-decort/internal/constants"
"github.com/rudecs/terraform-provider-decort/internal/controller"
log "github.com/sirupsen/logrus"
)
func resourceSnapshotCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceSnapshotCreate: called for snapshot %s", d.Get("label").(string))
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("label", d.Get("label").(string))
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
snapshotId, err := c.DecortAPICall(ctx, "POST", snapshotCreateAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
snapshotId = strings.ReplaceAll(snapshotId, "\"", "")
d.SetId(snapshotId)
d.Set("guid", snapshotId)
diagnostics := resourceSnapshotRead(ctx, d, m)
if diagnostics != nil {
return diagnostics
}
return nil
}
func resourceSnapshotRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
snapshot, err := utilitySnapshotCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
d.Set("timestamp", snapshot.Timestamp)
d.Set("guid", snapshot.Guid)
d.Set("disks", snapshot.Disks)
d.Set("label", snapshot.Label)
return nil
}
func resourceSnapshotDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceSnapshotDelete: called for %s, id: %s", d.Get("label").(string), d.Id())
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
urlValues.Add("label", d.Get("label").(string))
_, err := c.DecortAPICall(ctx, "POST", snapshotDeleteAPI, urlValues)
if err != nil {
return diag.FromErr(err)
}
d.SetId("")
return nil
}
func resourceSnapshotExists(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
snapshot, err := utilitySnapshotCheckPresence(ctx, d, m)
if err != nil {
return false, err
}
if snapshot == nil {
return false, nil
}
return true, nil
}
func resourceSnapshotEdit(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
if d.HasChange("rollback") {
if d.Get("rollback").(bool) {
err := resourceSnapshotRollback(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
}
}
return nil
}
func resourceSnapshotRollback(ctx context.Context, d *schema.ResourceData, m interface{}) error {
c := m.(*controller.ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
urlValues.Add("label", d.Get("label").(string))
_, err := c.DecortAPICall(ctx, "POST", snapshotRollbackAPI, urlValues)
if err != nil {
return err
}
return nil
}
func resourceSnapshotSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"compute_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "ID of the compute instance to create snapshot for.",
},
"label": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "text label for snapshot. Must be unique among this compute snapshots.",
},
"rollback": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "is rollback the snapshot",
},
"disks": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"guid": {
Type: schema.TypeString,
Computed: true,
Description: "guid of the snapshot",
},
"timestamp": {
Type: schema.TypeInt,
Computed: true,
Description: "timestamp",
},
}
}
func ResourceSnapshot() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceSnapshotCreate,
ReadContext: resourceSnapshotRead,
UpdateContext: resourceSnapshotEdit,
DeleteContext: resourceSnapshotDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout60s,
Read: &constants.Timeout30s,
Update: &constants.Timeout60s,
Delete: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: resourceSnapshotSchemaMake(),
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save