parent
ce84733848
commit
92528adf2b
@ -1,267 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
// "net/url"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
"github.com/hashicorp/terraform/helper/validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
func flattenCompute(d *schema.ResourceData, comp_facts string) error {
|
|
||||||
// NOTE: this function modifies ResourceData argument - as such it should never be called
|
|
||||||
// from resourceComputeExists(...) method
|
|
||||||
model := ComputeGetResp{}
|
|
||||||
log.Printf("flattenCompute: ready to unmarshal string %q", comp_facts)
|
|
||||||
err := json.Unmarshal([]byte(comp_facts), &model)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("flattenCompute: model.ID %d, model.ResGroupID %d", model.ID, model.ResGroupID)
|
|
||||||
|
|
||||||
d.SetId(fmt.Sprintf("%d", model.ID))
|
|
||||||
d.Set("name", model.Name)
|
|
||||||
d.Set("rgid", model.ResGroupID)
|
|
||||||
d.Set("rg_name", model.ResGroupName)
|
|
||||||
d.Set("account_id", model.AccountID)
|
|
||||||
d.Set("account_name", model.AccountName)
|
|
||||||
d.Set("arch", model.Arch)
|
|
||||||
d.Set("cpu", model.Cpu)
|
|
||||||
d.Set("ram", model.Ram)
|
|
||||||
d.Set("boot_disk_size", model.BootDiskSize)
|
|
||||||
d.Set("image_id", model.ImageID)
|
|
||||||
d.Set("description", model.Desc)
|
|
||||||
d.Set("status", model.Status)
|
|
||||||
d.Set("tech_status", model.TechStatus)
|
|
||||||
|
|
||||||
bootdisk_map := make(map[string]interface{})
|
|
||||||
bootdisk_map["size"] = model.BootDisk
|
|
||||||
bootdisk_map["label"] = "boot"
|
|
||||||
bootdisk_map["pool"] = "default"
|
|
||||||
bootdisk_map["provider"] = "default"
|
|
||||||
|
|
||||||
if err = d.Set("boot_disk", []interface{}{bootdisk_map}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(model.DataDisks) > 0 {
|
|
||||||
log.Printf("flattenCompute: calling flattenDataDisks")
|
|
||||||
if err = d.Set("data_disks", flattenDataDisks(model.DataDisks)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(model.NICs) > 0 {
|
|
||||||
log.Printf("flattenCompute: calling flattenNICs")
|
|
||||||
if err = d.Set("nics", flattenNICs(model.NICs)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Printf("flattenCompute: calling flattenNetworks")
|
|
||||||
if err = d.Set("networks", flattenNetworks(model.NICs)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(model.GuestLogins) > 0 {
|
|
||||||
log.Printf("flattenCompute: calling flattenGuestLogins")
|
|
||||||
guest_logins := flattenGuestLogins(model.GuestLogins)
|
|
||||||
if err = d.Set("guest_logins", guest_logins); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
default_login := guest_logins[0].(map[string]interface{})
|
|
||||||
// set user & password attributes to the corresponding values of the 1st item in the list
|
|
||||||
if err = d.Set("user", default_login["login"]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = d.Set("password", default_login["password"]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dataSourceComputeRead(d *schema.ResourceData, m interface{}) error {
|
|
||||||
comp_facts, err := utilityComputeCheckPresence(d, m)
|
|
||||||
if comp_facts == "" {
|
|
||||||
// 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 err
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattenCompute(d, comp_facts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func dataSourceCompute() *schema.Resource {
|
|
||||||
return &schema.Resource {
|
|
||||||
SchemaVersion: 1,
|
|
||||||
|
|
||||||
Read: dataSourceComputeRead,
|
|
||||||
|
|
||||||
Timeouts: &schema.ResourceTimeout {
|
|
||||||
Read: &Timeout30s,
|
|
||||||
Default: &Timeout60s,
|
|
||||||
},
|
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema {
|
|
||||||
"name": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Required: true,
|
|
||||||
Description: "Name of this compute instance. NOTE: this parameter is case sensitive.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"rgid": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Required: true,
|
|
||||||
ValidateFunc: validation.IntAtLeast(1),
|
|
||||||
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.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"arch": {
|
|
||||||
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.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"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).",
|
|
||||||
},
|
|
||||||
|
|
||||||
"guest_logins": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Computed: true,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: guestLoginsSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Details about the guest OS users provisioned together with this compute instance.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"networks": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Computed: true,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: networkSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Specification for the networks to connect this virtual machine to.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"nics": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Computed: true,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: nicSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Specification for the virutal NICs allocated to this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"description": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "User-defined text description of this compute instance.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"status": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Current model status of this compute instance.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"tech_status": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Current technical status of this compute instance.",
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
"internal_ip": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Internal IP address of this Compute.",
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,204 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
// "net/url"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
"github.com/hashicorp/terraform/helper/validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
func flattenDisk(d *schema.ResourceData, disk_facts string) error {
|
|
||||||
// NOTE: this function modifies ResourceData argument - as such it should never be called
|
|
||||||
// from resourceComputeExists(...) method
|
|
||||||
model := DiskRecord{}
|
|
||||||
log.Debugf("flattenDisk: ready to unmarshal string %q", disk_facts)
|
|
||||||
err := json.Unmarshal([]byte(disk_facts), &model)
|
|
||||||
if err != nil {
|
|
||||||
return 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)
|
|
||||||
d.Set("name", model.Name)
|
|
||||||
d.Set("account_id", model.AccountID)
|
|
||||||
d.Set("account_name", model.AccountName)
|
|
||||||
d.Set("size", model.SizeMax)
|
|
||||||
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)
|
|
||||||
d.Set("status", model.Status)
|
|
||||||
d.Set("tech_status", model.TechStatus)
|
|
||||||
|
|
||||||
/* we do not manage snapshots via Terraform yet, so keep this commented out for a while
|
|
||||||
if len(model.Snapshots) > 0 {
|
|
||||||
log.Debugf("flattenDisk: calling flattenDiskSnapshots")
|
|
||||||
if err = d.Set("nics", flattenDiskSnapshots(model.Snapshots)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dataSourceDiskRead(d *schema.ResourceData, m interface{}) error {
|
|
||||||
disk_facts, err := utilityDiskCheckPresence(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 err
|
|
||||||
}
|
|
||||||
|
|
||||||
return 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.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"disk_id": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Optional: true,
|
|
||||||
Description: "ID of the disk to get. If disk ID is specified, then name, account and account ID are ignored.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"account_id": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Optional: true,
|
|
||||||
Description: "ID of the account this disk belongs to.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"account_name": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "Name of the account this disk belongs to. If account ID is specified, account name is ignored.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"description": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "User-defined text description of this disk.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"image_id": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Computed: true,
|
|
||||||
Description: "ID of the image, which this disk was cloned from.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"size": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Size of the disk in GB.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"type": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Type of this disk.",
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
"snapshots": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Computed: true,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: snapshotSubresourceSchemaMake(),
|
|
||||||
},
|
|
||||||
Description: "List of user-created snapshots for this disk."
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
|
|
||||||
"sep_id": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Storage end-point provider serving this disk.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"sep_type": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Type of the storage end-point provider serving this disk.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"pool": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Pool where this disk is located.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"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: schema.TypeInt,
|
|
||||||
Computed: true,
|
|
||||||
Description: "ID of the compute instance where this disk is attached to, or 0 for unattached disk.",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func dataSourceDisk() *schema.Resource {
|
|
||||||
return &schema.Resource {
|
|
||||||
SchemaVersion: 1,
|
|
||||||
|
|
||||||
Read: dataSourceDiskRead,
|
|
||||||
|
|
||||||
Timeouts: &schema.ResourceTimeout {
|
|
||||||
Read: &Timeout30s,
|
|
||||||
Default: &Timeout60s,
|
|
||||||
},
|
|
||||||
|
|
||||||
Schema: dataSourceDiskSchemaMake(),
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2020 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
"github.com/hashicorp/terraform/helper/validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func dataSourceImageRead(d *schema.ResourceData, m interface{}) error {
|
|
||||||
name := d.Get("name").(string)
|
|
||||||
rgid, rgid_set := d.GetOk("rgid")
|
|
||||||
tenant_id, tenant_set := d.GetOk("tenant_id")
|
|
||||||
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
url_values := &url.Values{}
|
|
||||||
if tenant_set {
|
|
||||||
url_values.Add("accountId", fmt.Sprintf("%d",tenant_id.(int)))
|
|
||||||
}
|
|
||||||
if rgid_set {
|
|
||||||
url_values.Add("cloudspaceId", fmt.Sprintf("%d",rgid.(int)))
|
|
||||||
}
|
|
||||||
body_string, err := controller.decortAPICall("POST", ImagesListAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("dataSourceImageRead: ready to decode response body")
|
|
||||||
model := ImagesListResp{}
|
|
||||||
err = json.Unmarshal([]byte(body_string), &model)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("%#v", model)
|
|
||||||
log.Printf("dataSourceImageRead: traversing decoded JSON of length %d", len(model))
|
|
||||||
for index, item := range model {
|
|
||||||
// need to match VM by name
|
|
||||||
if item.Name == name {
|
|
||||||
log.Printf("dataSourceImageRead: index %d, matched name %q", index, item.Name)
|
|
||||||
d.SetId(fmt.Sprintf("%d", model[index].ID))
|
|
||||||
// d.Set("field_name", value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("Cannot find OS Image name %q", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func dataSourceImage() *schema.Resource {
|
|
||||||
return &schema.Resource {
|
|
||||||
SchemaVersion: 1,
|
|
||||||
|
|
||||||
Read: dataSourceImageRead,
|
|
||||||
|
|
||||||
Timeouts: &schema.ResourceTimeout {
|
|
||||||
Read: &Timeout30s,
|
|
||||||
Default: &Timeout60s,
|
|
||||||
},
|
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema {
|
|
||||||
"name": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Required: true,
|
|
||||||
Description: "Name of the OS image to locate. This parameter is case sensitive.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"tenant_id": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Optional: true,
|
|
||||||
ValidateFunc: validation.IntAtLeast(1),
|
|
||||||
Description: "ID of the tenant to limit image search to.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"rgid": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Optional: true,
|
|
||||||
ValidateFunc: validation.IntAtLeast(1),
|
|
||||||
Description: "ID of the resource group to limit image search to.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,175 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
// "net/url"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
// "github.com/hashicorp/terraform/helper/validation"
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
func flattenResgroup(d *schema.ResourceData, rg_facts string) error {
|
|
||||||
// NOTE: this function modifies ResourceData argument - as such it should never be called
|
|
||||||
// from resourceRsgroupExists(...) method
|
|
||||||
log.Debugf("%s", rg_facts)
|
|
||||||
log.Debugf("flattenResgroup: ready to decode response body from %q", CloudspacesGetAPI)
|
|
||||||
details := ResgroupGetResp{}
|
|
||||||
err := json.Unmarshal([]byte(rg_facts), &details)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("flattenResgroup: decoded ResGroup name %q / ID %d, account ID %d, public IP %q",
|
|
||||||
details.Name, details.ID, details.AccountID, details.PublicIP)
|
|
||||||
|
|
||||||
d.SetId(fmt.Sprintf("%d", details.ID))
|
|
||||||
d.Set("name", details.Name)
|
|
||||||
d.Set("account_id", details.AccountID)
|
|
||||||
d.Set("grid_id", details.GridID)
|
|
||||||
d.Set("desc", details.Description)
|
|
||||||
d.Set("status", details.Status)
|
|
||||||
d.Set("def_net", details.DefaultNetType)
|
|
||||||
d.Set("def_net_id", details.DefaultNetID)
|
|
||||||
d.Set("vins", details.Vins)
|
|
||||||
d.Set("computes", details.Computes)
|
|
||||||
|
|
||||||
log.Debugf("flattenResgroup: calling flattenQuota()")
|
|
||||||
if err = d.Set("quotas", flattenQuota(details.Quotas)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dataSourceResgroupRead(d *schema.ResourceData, m interface{}) error {
|
|
||||||
rg_facts, err := utilityResgroupCheckPresence(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 err
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattenResgroup(d, rg_facts)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func dataSourceResgroup() *schema.Resource {
|
|
||||||
return &schema.Resource {
|
|
||||||
SchemaVersion: 1,
|
|
||||||
|
|
||||||
Read: dataSourceResgroupRead,
|
|
||||||
|
|
||||||
Timeouts: &schema.ResourceTimeout {
|
|
||||||
Read: &Timeout30s,
|
|
||||||
Default: &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 an account.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"account": &schema.Schema {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Required: true,
|
|
||||||
Description: "Name of the account, which this resource group belongs to.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"account_id": &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Unique ID of the account, which this resource group belongs to.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"desc": &schema.Schema {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "User-defined text description of this resource group.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"grid_id": &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Unique ID of the grid, where this resource group is deployed.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"quotas": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: quotaRgSubresourceSchema(), // this is a dictionary
|
|
||||||
},
|
|
||||||
Description: "Quotas on the resources for this resource group.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"status": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Current status of this resource group.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"def_net": &schema.Schema {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Type of the default network for this resource group.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"def_net_id": &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Computed: true,
|
|
||||||
Description: "ID of the default network for this resource group (if any).",
|
|
||||||
},
|
|
||||||
|
|
||||||
"vins": {
|
|
||||||
Type: schema.TypeList, // this is a list of ints
|
|
||||||
Computed: true,
|
|
||||||
MaxItems: LimitMaxVinsPerResgroup,
|
|
||||||
Elem: &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
},
|
|
||||||
Description: "List of VINs deployed in this resource group.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"computes": {
|
|
||||||
Type: schema.TypeList, //t his is a list of ints
|
|
||||||
Computed: true,
|
|
||||||
Elem: &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
},
|
|
||||||
Description: "List of computes deployed in this resource group.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
type DiskConfig struct {
|
|
||||||
Label string
|
|
||||||
Size int
|
|
||||||
Pool string
|
|
||||||
Provider string
|
|
||||||
ID int
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetworkConfig struct {
|
|
||||||
Label string
|
|
||||||
NetworkID int
|
|
||||||
}
|
|
||||||
|
|
||||||
type PortforwardConfig struct {
|
|
||||||
Label string
|
|
||||||
ExtPort int
|
|
||||||
IntPort int
|
|
||||||
Proto string
|
|
||||||
}
|
|
||||||
|
|
||||||
type SshKeyConfig struct {
|
|
||||||
User string
|
|
||||||
SshKey string
|
|
||||||
UserShell string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComputeConfig struct {
|
|
||||||
ResGroupID int
|
|
||||||
Name string
|
|
||||||
ID int
|
|
||||||
Cpu int
|
|
||||||
Ram int
|
|
||||||
ImageID int
|
|
||||||
BootDisk DiskConfig
|
|
||||||
DataDisks []DiskConfig
|
|
||||||
Networks []NetworkConfig
|
|
||||||
PortForwards []PortforwardConfig
|
|
||||||
SshKeys []SshKeyConfig
|
|
||||||
Description string
|
|
||||||
// The following two parameters are required to create data disks by
|
|
||||||
// a separate disks/create API call
|
|
||||||
AccountID int
|
|
||||||
GridID int
|
|
||||||
// The following one paratmeter is required to create port forwards
|
|
||||||
// it will be obsoleted when we implement true Resource Groups
|
|
||||||
ExtIP string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResgroupQuotaConfig struct {
|
|
||||||
Cpu int
|
|
||||||
Ram float32 // NOTE: it is float32! However, int would be enough here
|
|
||||||
Disk int
|
|
||||||
NetTraffic int
|
|
||||||
ExtIPs int
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResgroupConfig struct {
|
|
||||||
AccountID int
|
|
||||||
AccountName string
|
|
||||||
Location string
|
|
||||||
Name string
|
|
||||||
ID int
|
|
||||||
GridID int
|
|
||||||
ExtIP string // legacy field for VDC - this will eventually become obsoleted by true Resource Groups
|
|
||||||
Quota ResgroupQuotaConfig
|
|
||||||
Network NetworkConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
@ -1,485 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2020 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
"github.com/hashicorp/terraform/helper/validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func resourceComputeCreate(d *schema.ResourceData, m interface{}) error {
|
|
||||||
machine := &MachineConfig{
|
|
||||||
ResGroupID: d.Get("rgid").(int),
|
|
||||||
Name: d.Get("name").(string),
|
|
||||||
Cpu: d.Get("cpu").(int),
|
|
||||||
Ram: d.Get("ram").(int),
|
|
||||||
ImageID: d.Get("image_id").(int),
|
|
||||||
Description: d.Get("description").(string),
|
|
||||||
}
|
|
||||||
// BootDisk
|
|
||||||
// DataDisks
|
|
||||||
// Networks
|
|
||||||
// PortForwards
|
|
||||||
// SshKeyData string
|
|
||||||
log.Printf("resourceComputeCreate: called for VM name %q, ResGroupID %d", machine.Name, machine.ResGroupID)
|
|
||||||
|
|
||||||
var subres_list []interface{}
|
|
||||||
var subres_data map[string]interface{}
|
|
||||||
var arg_value interface{}
|
|
||||||
var arg_set bool
|
|
||||||
// boot disk list is a required argument and has only one element,
|
|
||||||
// which is of type diskSubresourceSchema
|
|
||||||
subres_list = d.Get("boot_disk").([]interface{})
|
|
||||||
subres_data = subres_list[0].(map[string]interface{})
|
|
||||||
machine.BootDisk.Label = subres_data["label"].(string)
|
|
||||||
machine.BootDisk.Size = subres_data["size"].(int)
|
|
||||||
machine.BootDisk.Pool = subres_data["pool"].(string)
|
|
||||||
machine.BootDisk.Provider = subres_data["provider"].(string)
|
|
||||||
|
|
||||||
|
|
||||||
arg_value, arg_set = d.GetOk("data_disks")
|
|
||||||
if arg_set {
|
|
||||||
log.Printf("resourceComputeCreate: calling makeDisksConfig")
|
|
||||||
machine.DataDisks, _ = makeDisksConfig(arg_value.([]interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_value, arg_set = d.GetOk("networks")
|
|
||||||
if arg_set {
|
|
||||||
log.Printf("resourceComputeCreate: calling makeNetworksConfig")
|
|
||||||
machine.Networks, _ = makeNetworksConfig(arg_value.([]interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_value, arg_set = d.GetOk("port_forwards")
|
|
||||||
if arg_set {
|
|
||||||
log.Printf("resourceComputeCreate: calling makePortforwardsConfig")
|
|
||||||
machine.PortForwards, _ = makePortforwardsConfig(arg_value.([]interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_value, arg_set = d.GetOk("ssh_keys")
|
|
||||||
if arg_set {
|
|
||||||
log.Printf("resourceComputeCreate: calling makeSshKeysConfig")
|
|
||||||
machine.SshKeys, _ = makeSshKeysConfig(arg_value.([]interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// create basic VM (i.e. without port forwards and ext network connections - those will be done
|
|
||||||
// by separate API calls)
|
|
||||||
d.Partial(true)
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
url_values := &url.Values{}
|
|
||||||
url_values.Add("cloudspaceId", fmt.Sprintf("%d", machine.ResGroupID))
|
|
||||||
url_values.Add("name", machine.Name)
|
|
||||||
url_values.Add("description", machine.Description)
|
|
||||||
url_values.Add("vcpus", fmt.Sprintf("%d", machine.Cpu))
|
|
||||||
url_values.Add("memory", fmt.Sprintf("%d", machine.Ram))
|
|
||||||
url_values.Add("imageId", fmt.Sprintf("%d", machine.ImageID))
|
|
||||||
url_values.Add("disksize", fmt.Sprintf("%d", machine.BootDisk.Size))
|
|
||||||
if len(machine.SshKeys) > 0 {
|
|
||||||
url_values.Add("userdata", makeSshKeysArgString(machine.SshKeys))
|
|
||||||
}
|
|
||||||
api_resp, err := controller.decortAPICall("POST", MachineCreateAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.SetId(api_resp) // machines/create API plainly returns ID of the new VM on success
|
|
||||||
machine.ID, _ = strconv.Atoi(api_resp)
|
|
||||||
d.SetPartial("name")
|
|
||||||
d.SetPartial("description")
|
|
||||||
d.SetPartial("cpu")
|
|
||||||
d.SetPartial("ram")
|
|
||||||
d.SetPartial("image_id")
|
|
||||||
d.SetPartial("boot_disk")
|
|
||||||
if len(machine.SshKeys) > 0 {
|
|
||||||
d.SetPartial("ssh_keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("resourceComputeCreate: new VM ID %d, name %q created", machine.ID, machine.Name)
|
|
||||||
|
|
||||||
if len(machine.DataDisks) > 0 || len(machine.PortForwards) > 0 {
|
|
||||||
// for data disk or port foreards provisioning we have to know Tenant ID
|
|
||||||
// and Grid ID so we call utilityResgroupConfigGet method to populate these
|
|
||||||
// fields in the machine structure that will be passed to provisionVmDisks or
|
|
||||||
// provisionVmPortforwards
|
|
||||||
log.Printf("resourceComputeCreate: calling utilityResgroupConfigGet")
|
|
||||||
resgroup, err := controller.utilityResgroupConfigGet(machine.ResGroupID)
|
|
||||||
if err == nil {
|
|
||||||
machine.TenantID = resgroup.TenantID
|
|
||||||
machine.GridID = resgroup.GridID
|
|
||||||
machine.ExtIP = resgroup.ExtIP
|
|
||||||
log.Printf("resourceComputeCreate: tenant ID %d, GridID %d, ExtIP %q",
|
|
||||||
machine.TenantID, machine.GridID, machine.ExtIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Configure data disks
|
|
||||||
disks_ok := true
|
|
||||||
if len(machine.DataDisks) > 0 {
|
|
||||||
log.Printf("resourceComputeCreate: calling utilityVmDisksProvision for disk count %d", len(machine.DataDisks))
|
|
||||||
if machine.TenantID == 0 {
|
|
||||||
// if TenantID is still 0 it means that we failed to get Resgroup Facts by
|
|
||||||
// a previous call to utilityResgroupGetFacts,
|
|
||||||
// hence we do not have technical ability to provision data disks
|
|
||||||
disks_ok = false
|
|
||||||
} else {
|
|
||||||
// provisionVmDisks accomplishes two steps for each data disk specification
|
|
||||||
// 1) creates the disks
|
|
||||||
// 2) attaches them to the VM
|
|
||||||
err = controller.utilityVmDisksProvision(machine)
|
|
||||||
if err != nil {
|
|
||||||
disks_ok = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if disks_ok {
|
|
||||||
d.SetPartial("data_disks")
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Configure port forward rules
|
|
||||||
pfws_ok := true
|
|
||||||
if len(machine.PortForwards) > 0 {
|
|
||||||
log.Printf("resourceComputeCreate: calling utilityVmPortforwardsProvision for pfw rules count %d", len(machine.PortForwards))
|
|
||||||
if machine.ExtIP == "" {
|
|
||||||
// if ExtIP is still empty it means that we failed to get Resgroup Facts by
|
|
||||||
// a previous call to utilityResgroupGetFacts,
|
|
||||||
// hence we do not have technical ability to provision port forwards
|
|
||||||
pfws_ok = false
|
|
||||||
} else {
|
|
||||||
err := controller.utilityVmPortforwardsProvision(machine)
|
|
||||||
if err != nil {
|
|
||||||
pfws_ok = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pfws_ok {
|
|
||||||
// there were no errors reported when configuring port forwards
|
|
||||||
d.SetPartial("port_forwards")
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Configure external networks
|
|
||||||
// NOTE: currently only one external network can be attached to each VM, so in the current
|
|
||||||
// implementation we ignore all but the 1st network definition
|
|
||||||
nets_ok := true
|
|
||||||
if len(machine.Networks) > 0 {
|
|
||||||
log.Printf("resourceComputeCreate: calling utilityVmNetworksProvision for networks count %d", len(machine.Networks))
|
|
||||||
err := controller.utilityVmNetworksProvision(machine)
|
|
||||||
if err != nil {
|
|
||||||
nets_ok = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if nets_ok {
|
|
||||||
// there were no errors reported when configuring networks
|
|
||||||
d.SetPartial("networks")
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( disks_ok && nets_ok && pfws_ok ) {
|
|
||||||
// if there were no errors in setting any of the subresources, we may leave Partial mode
|
|
||||||
d.Partial(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// resourceComputeRead will also update resource ID on success, so that Terraform will know
|
|
||||||
// that resource exists
|
|
||||||
return resourceComputeRead(d, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceComputeRead(d *schema.ResourceData, m interface{}) error {
|
|
||||||
log.Printf("resourceComputeRead: called for VM name %q, ResGroupID %d",
|
|
||||||
d.Get("name").(string), d.Get("rgid").(int))
|
|
||||||
|
|
||||||
comp_facts, err := utilityComputeCheckPresence(d, m)
|
|
||||||
if comp_facts == "" {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// VM was not found
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = flattenCompute(d, comp_facts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Printf("resourceComputeRead: after flattenCompute: VM ID %s, VM name %q, ResGroupID %d",
|
|
||||||
d.Id(), d.Get("name").(string), d.Get("rgid").(int))
|
|
||||||
|
|
||||||
// Not all parameters, that we may need, are returned by machines/get API
|
|
||||||
// Continue with further reading of VM subresource parameters:
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
url_values := &url.Values{}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Obtain information on external networks
|
|
||||||
url_values.Add("machineId", d.Id())
|
|
||||||
body_string, err := controller.decortAPICall("POST", VmExtNetworksListAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
net_list := ExtNetworksResp{}
|
|
||||||
err = json.Unmarshal([]byte(body_string), &net_list)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(net_list) > 0 {
|
|
||||||
if err = d.Set("networks", flattenNetworks(net_list)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Ext networks flattening is now done inside flattenCompute because it is currently based
|
|
||||||
// on data read into NICs component by machine/get API call
|
|
||||||
|
|
||||||
if err = d.Set("networks", flattenNetworks()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// Obtain information on port forwards
|
|
||||||
url_values.Add("cloudspaceId", fmt.Sprintf("%d",d.Get("rgid")))
|
|
||||||
url_values.Add("machineId", d.Id())
|
|
||||||
pfw_list := PortforwardsResp{}
|
|
||||||
body_string, err := controller.decortAPICall("POST", PortforwardsListAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal([]byte(body_string), &pfw_list)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pfw_list) > 0 {
|
|
||||||
if err = d.Set("port_forwards", flattenPortforwards(pfw_list)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceComputeUpdate(d *schema.ResourceData, m interface{}) error {
|
|
||||||
log.Printf("resourceComputeUpdate: called for VM name %q, ResGroupID %d",
|
|
||||||
d.Get("name").(string), d.Get("rgid").(int))
|
|
||||||
|
|
||||||
return resourceComputeRead(d, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceComputeDelete(d *schema.ResourceData, m interface{}) error {
|
|
||||||
// NOTE: this method destroys target VM with flag "permanently", so there is no way to
|
|
||||||
// restore destroyed VM
|
|
||||||
log.Printf("resourceComputeDelete: called for VM name %q, ResGroupID %d",
|
|
||||||
d.Get("name").(string), d.Get("rgid").(int))
|
|
||||||
|
|
||||||
comp_facts, err := utilityComputeCheckPresence(d, m)
|
|
||||||
if comp_facts == "" {
|
|
||||||
// the target VM 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("machineId", d.Id())
|
|
||||||
params.Add("permanently", "true")
|
|
||||||
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
comp_facts, err = controller.decortAPICall("POST", MachineDeleteAPI, params)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceComputeExists(d *schema.ResourceData, m interface{}) (bool, error) {
|
|
||||||
// Reminder: according to Terraform rules, this function should not modify its ResourceData argument
|
|
||||||
log.Printf("resourceComputeExist: called for VM name %q, ResGroupID %d",
|
|
||||||
d.Get("name").(string), d.Get("rgid").(int))
|
|
||||||
|
|
||||||
comp_facts, err := utilityComputeCheckPresence(d, m)
|
|
||||||
if comp_facts == "" {
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceCompute() *schema.Resource {
|
|
||||||
return &schema.Resource {
|
|
||||||
SchemaVersion: 1,
|
|
||||||
|
|
||||||
Create: resourceComputeCreate,
|
|
||||||
Read: resourceComputeRead,
|
|
||||||
Update: resourceComputeUpdate,
|
|
||||||
Delete: resourceComputeDelete,
|
|
||||||
Exists: resourceComputeExists,
|
|
||||||
|
|
||||||
Timeouts: &schema.ResourceTimeout {
|
|
||||||
Create: &Timeout180s,
|
|
||||||
Read: &Timeout30s,
|
|
||||||
Update: &Timeout180s,
|
|
||||||
Delete: &Timeout60s,
|
|
||||||
Default: &Timeout60s,
|
|
||||||
},
|
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema {
|
|
||||||
"name": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Required: true,
|
|
||||||
Description: "Name of this virtual machine. This parameter is case sensitive.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"rgid": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Required: true,
|
|
||||||
ValidateFunc: validation.IntAtLeast(1),
|
|
||||||
Description: "ID of the resource group where this virtual machine should be deployed.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"cpu": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Required: true,
|
|
||||||
ValidateFunc: validation.IntBetween(1, 64),
|
|
||||||
Description: "Number of CPUs to allocate to this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"ram": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Required: true,
|
|
||||||
ValidateFunc: validation.IntAtLeast(512),
|
|
||||||
Description: "Amount of RAM in MB to allocate to this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"image_id": {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Required: true,
|
|
||||||
ForceNew: true,
|
|
||||||
Description: "ID of the OS image to base this virtual machine on.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"boot_disk": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Required: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: diskSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Specification for a boot disk on this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"data_disks": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 12,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: diskSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Specification for data disks on this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"guest_logins": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Computed: true,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: loginsSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Specification for guest logins on this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"networks": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 8,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: networkSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Specification for the networks to connect this virtual machine to.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"nics": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Computed: true,
|
|
||||||
MaxItems: 8,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: nicSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Specification for the virutal NICs allocated to this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"ssh_keys": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 12,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: sshSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "SSH keys to authorize on this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"port_forwards": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 12,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: portforwardSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Specification for the port forwards to configure for this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"description": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "Description of this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"user": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Default login name for the guest OS on this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"password": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Sensitive: true,
|
|
||||||
Description: "Default password for the guest OS login on this virtual machine.",
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,401 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
|
|
||||||
// First validate that we have all parameters required to create the new Resource Group
|
|
||||||
arg_set := false
|
|
||||||
account_name, arg_set := d.GetOk("account")
|
|
||||||
if !arg_set {
|
|
||||||
return fmt.Errorf("Cannot create new RG: missing account.")
|
|
||||||
}
|
|
||||||
rg_name, arg_set := d.GetOk("name")
|
|
||||||
if !arg_set {
|
|
||||||
return fmt.Errorf("Cannot create new RG: missing name.")
|
|
||||||
}
|
|
||||||
grid_id, arg_set := d.GetOk("grid_id")
|
|
||||||
if !arg_set {
|
|
||||||
return fmt.Errorf("Cannot create new RG %q for account %q: missing Grid ID.",
|
|
||||||
rg_name.(string), account_name.(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
// all required parameters are set in the schema - we can continue with RG creation
|
|
||||||
log.Debugf("resourceResgroupCreate: called for RG name %q, account name %q",
|
|
||||||
account_name.(string), rg_name.(string))
|
|
||||||
|
|
||||||
// Valid account ID is required to create new resource group
|
|
||||||
// obtain Account ID by account name - it should not be zero on success
|
|
||||||
validated_account_id, err := utilityGetAccountIdByName(account_name.(string), m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
log.Debugf("resourceResgroupCreate: called by user %q for RG name %q, account %q / ID %d, Grid ID %d",
|
|
||||||
controller.getdecortUsername(),
|
|
||||||
rg_name.(string), account_name.(string), validated_account_id, gird_id.(int))
|
|
||||||
/*
|
|
||||||
type ResgroupCreateParam struct {
|
|
||||||
AccountID int `json:"accountId"`
|
|
||||||
GridId int `json:"gid"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Ram int `json:"maxMemoryCapacity"`
|
|
||||||
Disk int `json:"maxVDiskCapacity"`
|
|
||||||
Cpu int `json:"maxCPUCapacity"`
|
|
||||||
NetTraffic int `json:"maxNetworkPeerTransfer"`
|
|
||||||
ExtIPs int `json:"maxNumPublicIP"`
|
|
||||||
Owner string `json:"owner"`
|
|
||||||
DefNet string `json:"def_net"`
|
|
||||||
IPCidr string `json:"ipcidr"`
|
|
||||||
Desc string `json:"decs"`
|
|
||||||
Reason string `json:"reason"`
|
|
||||||
ExtNetID int `json:"extNetId"`
|
|
||||||
ExtIP string `json:"extIp"`
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
url_values := &url.Values{}
|
|
||||||
url_values.Add("accountId", fmt.Sprintf("%d", validated_account_id))
|
|
||||||
url_values.Add("name", rg_name.(string))
|
|
||||||
url_values.Add("gid", fmt.Sprintf("%d", grid_id.(int)))
|
|
||||||
url_values.Add("owner", controller.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("%d", quota_record.Ram))
|
|
||||||
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 {
|
|
||||||
ulr_values.Add("def_net", def_net_type.(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcidr, arg_set = d.GetOk("ipcidr")
|
|
||||||
if arg_set {
|
|
||||||
ulr_values.Add("ipcidr", ipcidr.(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_net_id, arg_set = d.GetOk("ext_net_id")
|
|
||||||
if arg_set {
|
|
||||||
ulr_values.Add("extNetId", ext_net_id.(int))
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_ip, arg_set = d.GetOk("ext_ip")
|
|
||||||
if arg_set {
|
|
||||||
ulr_values.Add("extIp", ext_ip.(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
api_resp, err := controller.decortAPICall("POST", ResgroupCreateAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.SetId(api_resp) // rg/create API returns ID of the newly creted resource group on success
|
|
||||||
rg.ID, _ = strconv.Atoi(api_resp)
|
|
||||||
|
|
||||||
// re-read newly created RG to make sure schema contains complete and up to date set of specifications
|
|
||||||
return resourceResgroupRead(d, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceResgroupRead(d *schema.ResourceData, m interface{}) error {
|
|
||||||
log.Debugf("resourceResgroupRead: called for RG name %q, account name %q",
|
|
||||||
d.Get("name").(string), d.Get("account").(string))
|
|
||||||
rg_facts, err := utilityResgroupCheckPresence(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 err
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattenResgroup(d, rg_facts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
|
|
||||||
log.Debugf("resourceResgroupUpdate: called for RG name %q, account name %q",
|
|
||||||
d.Get("name").(string), d.Get("account").(string))
|
|
||||||
|
|
||||||
do_update := false
|
|
||||||
|
|
||||||
controller := m.(*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_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_update = true
|
|
||||||
log.Debugf("resourceResgroupUpdate: Cpu diff %d <- %d", quotarecord_new.Cpu, quotarecord_old.Cpu)
|
|
||||||
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quotarecord_new.Cpu))
|
|
||||||
}
|
|
||||||
|
|
||||||
if quotarecord_new.Disk != quotarecord_old.Disk {
|
|
||||||
do_update = true
|
|
||||||
log.Debugf("resourceResgroupUpdate: Disk diff %d <- %d", quotarecord_new.Disk, quotarecord_old.Disk)
|
|
||||||
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotarecord_new.Disk))
|
|
||||||
}
|
|
||||||
|
|
||||||
if quotarecord_new.Ram != quotarecord_old.Ram {
|
|
||||||
do_update = true
|
|
||||||
log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotarecord_new.Ram, quotarecord_old.Ram)
|
|
||||||
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotarecord_new.Ram))
|
|
||||||
}
|
|
||||||
|
|
||||||
if quotarecord_new.ExtTraffic != quotarecord_old.ExtTraffic {
|
|
||||||
do_update = true
|
|
||||||
log.Debugf("resourceResgroupUpdate: NetTraffic diff %d <- %d", quotarecord_new.ExtTraffic, quotarecord_old.ExtTraffic)
|
|
||||||
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quotarecord_new.NetTraffic))
|
|
||||||
}
|
|
||||||
|
|
||||||
if quotarecord_new.ExtIPs != quotarecord_old.ExtIPs {
|
|
||||||
do_update = true
|
|
||||||
log.Debugf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotarecord_new.ExtIPs, quotarecord_old.ExtIPs)
|
|
||||||
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quotarecord_new.ExtIPs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
desc_new, desc_set := d.GetOk("desc")
|
|
||||||
if desc_set {
|
|
||||||
log.Debugf("resourceResgroupUpdate: description specified - looking for deltas from the old settings.")
|
|
||||||
desc_old, _ := d.GetChange("desc")
|
|
||||||
if desc_old.(string) != desc_new.(string) {
|
|
||||||
do_update := true
|
|
||||||
url_values.Add("desc", desc_new.(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if do_update {
|
|
||||||
log.Debugf("resourceResgroupUpdate: detected delta between new and old RG specs - updating the RG")
|
|
||||||
_, err := controller.decortAPICall("POST", ResgroupUpdateAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Debugf("resourceResgroupUpdate: no difference between old and new state - no update on the RG will be done")
|
|
||||||
}
|
|
||||||
|
|
||||||
return resourceResgroupRead(d, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceResgroupDelete(d *schema.ResourceData, m interface{}) error {
|
|
||||||
// NOTE: this method forcibly destroys target resource group with flag "permanently", so there is no way to
|
|
||||||
// restore the destroyed resource group as well all Computes & VINSes that existed in it
|
|
||||||
log.Debugf("resourceResgroupDelete: called for RG name %q, account name %q",
|
|
||||||
d.Get("name").(string), d.Get("account").(string))
|
|
||||||
|
|
||||||
rg_facts, err := utilityResgroupCheckPresence(d, m)
|
|
||||||
if rg_facts == "" {
|
|
||||||
// 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", "true")
|
|
||||||
url_values.Add("permanently", "true")
|
|
||||||
url_values.Add("reason", "Destroyed by DECORT Terraform provider")
|
|
||||||
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
_, err = controller.decortAPICall("POST", ResgroupDeleteAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceResgroupExists(d *schema.ResourceData, m interface{}) (bool, error) {
|
|
||||||
// Reminder: according to Terraform rules, this function should NOT modify ResourceData argument
|
|
||||||
rg_facts, err := utilityResgroupCheckPresence(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,
|
|
||||||
|
|
||||||
Create: resourceResgroupCreate,
|
|
||||||
Read: resourceResgroupRead,
|
|
||||||
Update: resourceResgroupUpdate,
|
|
||||||
Delete: resourceResgroupDelete,
|
|
||||||
Exists: resourceResgroupExists,
|
|
||||||
|
|
||||||
Timeouts: &schema.ResourceTimeout {
|
|
||||||
Create: &Timeout180s,
|
|
||||||
Read: &Timeout30s,
|
|
||||||
Update: &Timeout180s,
|
|
||||||
Delete: &Timeout60s,
|
|
||||||
Default: &Timeout60s,
|
|
||||||
},
|
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema {
|
|
||||||
"name": &schema.Schema {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Required: true,
|
|
||||||
Description: "Name of this resource group. Names are case sensitive and unique within the context of a account.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"account": &schema.Schema {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Required: true,
|
|
||||||
Description: "Name of the account, which this resource group belongs to.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"def_net": &schema.Schema {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Default: "PRIVATE"
|
|
||||||
Description: "Type of the network, which this resource group will use as default for its computes - PRIVATE or PUBLIC or NONE.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"ipcidr": &schema.Schema {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "Address of the netowrk inside the private network segment (aka ViNS) if def_net=PRIVATE",
|
|
||||||
},
|
|
||||||
|
|
||||||
"ext_net_id": &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Optional: true,
|
|
||||||
Default: 0,
|
|
||||||
Description: "ID of the external network, which this resource group will use as default for its computes if def_net=PUBLIC",
|
|
||||||
},
|
|
||||||
|
|
||||||
"ext_ip": &schema.Schema {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "IP address on the external netowrk to request, if def_net=PUBLIC",
|
|
||||||
},
|
|
||||||
|
|
||||||
"account_id": &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Unique ID of the account, which this resource group belongs to.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"grid_id": &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Required: true,
|
|
||||||
Description: "Unique ID of the grid, where this resource group is deployed.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"quota": {
|
|
||||||
Type: schema.TypeList,
|
|
||||||
Optional: true,
|
|
||||||
MaxItems: 1,
|
|
||||||
Elem: &schema.Resource {
|
|
||||||
Schema: quotasSubresourceSchema(),
|
|
||||||
},
|
|
||||||
Description: "Quota settings for this resource group.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"desc": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "User-defined text description of this resource group.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"status": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
Description: "Current status of this resource group.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"def_net_id": &schema.Schema {
|
|
||||||
Type: schema.TypeInt,
|
|
||||||
Computed: true,
|
|
||||||
Description: "ID of the default network for this resource group (if any).",
|
|
||||||
},
|
|
||||||
|
|
||||||
"vins": {
|
|
||||||
Type: schema.TypeList, // 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.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
// "github.com/hashicorp/terraform/helper/validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func utilityDiskCheckPresence(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.
|
|
||||||
//
|
|
||||||
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
url_values := &url.Values{}
|
|
||||||
|
|
||||||
disk_id, arg_set := d.GetOk("disk_id")
|
|
||||||
if arg_set {
|
|
||||||
// go straight for the disk by its ID
|
|
||||||
log.Debugf("utilityDiskCheckPresence: locating disk by its ID %d", disk_id.(int))
|
|
||||||
url_values.Add("diskId", fmt.Sprintf("%d", disk_id.(int)))
|
|
||||||
disk_facts, err := controller.decortAPICall("POST", DisksGetAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return body_string, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
disk_name, arg_set := d.GetOk("name")
|
|
||||||
if !arg_set {
|
|
||||||
// no disk ID and no disk name - we cannot locate disk in this case
|
|
||||||
return "", fmt.Error("Cannot locate disk if name is empty and no disk ID specified.")
|
|
||||||
}
|
|
||||||
|
|
||||||
account_id, acc_id_set := d.GetOk("account_id")
|
|
||||||
if !acc_id_set {
|
|
||||||
account_name, arg_set := d.GetOkd("account_name")
|
|
||||||
if !arg_set {
|
|
||||||
return "", fmt.Error("Cannot locate disk by name %s if neither account ID nor account name are set", disk_name.(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
url_values.Add("accountId", fmt.Sprintf("%d", account_id.(int)))
|
|
||||||
disk_facts, err := controller.decortAPICall("POST", DisksListAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("utilityDiskCheckPresence: ready to unmarshal string %q", disk_facts)
|
|
||||||
|
|
||||||
disks_list := []DiskRecord
|
|
||||||
err = json.Unmarshal([]byte(disk_facts), &disks_list)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// log.Printf("%#v", vm_list)
|
|
||||||
log.Debugf("utilityDiskCheckPresence: traversing decoded JSON of length %d", len(disks_list))
|
|
||||||
for _, item := range disks_list {
|
|
||||||
// need to match disk by name, return the first match
|
|
||||||
if item.Name == disk_name && item.Status != "DESTROYED" {
|
|
||||||
log.Printf("utilityDiskCheckPresence: index %d, matched disk name %q", index, item.Name)
|
|
||||||
// we found the disk we need - not get detailed information via API call to disks/get
|
|
||||||
// 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_url_values := &url.Values{}
|
|
||||||
get_url_values.Add("diskId", fmt.Sprintf("%d", item.ID))
|
|
||||||
disk_facts, err = controller.decortAPICall("POST", DisksGetAPI, get_url_values)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return disk_facts, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", nil // there should be no error if disk does not exist
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/url"
|
|
||||||
// "strconv"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
// "github.com/hashicorp/terraform/helper/validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupGetResp, error) {
|
|
||||||
url_values := &url.Values{}
|
|
||||||
url_values.Add("rgId", fmt.Sprintf("%d", rgid))
|
|
||||||
resgroup_facts, err := ctrl.decortAPICall("POST", ResgroupGetAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("utilityResgroupConfigGet: ready to unmarshal string %q", resgroup_facts)
|
|
||||||
model := &ResgroupGetResp{}
|
|
||||||
err = json.Unmarshal([]byte(resgroup_facts), model)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
ret := &ResgroupConfig{}
|
|
||||||
ret.AccountID = model.AccountID
|
|
||||||
ret.Location = model.Location
|
|
||||||
ret.Name = model.Name
|
|
||||||
ret.ID = rgid
|
|
||||||
ret.GridID = model.GridID
|
|
||||||
ret.ExtIP = model.ExtIP // legacy field for VDC - this will eventually become obsoleted by true Resource Groups
|
|
||||||
// Quota ResgroupQuotaConfig
|
|
||||||
// Network NetworkConfig
|
|
||||||
*/
|
|
||||||
log.Debugf("utilityResgroupConfigGet: account ID %d, GridID %d, Name %s",
|
|
||||||
model.AccountID, model.GridID, model.Name)
|
|
||||||
|
|
||||||
return model, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// On success this function returns a string, as returned by API rg/get, which could be unmarshalled
|
|
||||||
// into ResgroupGetResp structure
|
|
||||||
func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
|
|
||||||
// This function tries to locate resource group by its name and account name.
|
|
||||||
// If succeeded, it returns non empty string that contains JSON formatted facts about the
|
|
||||||
// resource group as returned by cloudspaces/get API call.
|
|
||||||
// Otherwise it returns empty string and 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.
|
|
||||||
//
|
|
||||||
name := d.Get("name").(string)
|
|
||||||
account_name := d.Get("account").(string)
|
|
||||||
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
url_values := &url.Values{}
|
|
||||||
url_values.Add("includedeleted", "false")
|
|
||||||
body_string, err := controller.decortAPICall("POST", ResgroupListAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("%s", body_string)
|
|
||||||
log.Debugf("utilityResgroupCheckPresence: ready to decode response body from %q", ResgroupListAPI)
|
|
||||||
model := ResgroupListResp{}
|
|
||||||
err = json.Unmarshal([]byte(body_string), &model)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("utilityResgroupCheckPresence: traversing decoded Json of length %d", len(model))
|
|
||||||
for index, item := range model {
|
|
||||||
// need to match RG by name & account name
|
|
||||||
if item.Name == name && item.AccountName == account_name {
|
|
||||||
log.Debugf("utilityResgroupCheckPresence: match RG name %q / ID %d, account %q at index %d",
|
|
||||||
item.Name, item.ID, item.AccountName, index)
|
|
||||||
|
|
||||||
// not all required information is returned by rg/list API, so we need to initiate one more
|
|
||||||
// call to rg/get to obtain extra data to complete Resource population.
|
|
||||||
// Namely, we need to extract resource quota settings
|
|
||||||
req_values := &url.Values{}
|
|
||||||
req_values.Add("rgId", fmt.Sprintf("%d", item.ID))
|
|
||||||
body_string, err := controller.decortAPICall("POST", ResgroupGetAPI, req_values)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return body_string, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", fmt.Errorf("Cannot find RG name %q owned by account %q", name, account_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func utilityGetAccountIdByName(account_name string, m interface{}) (int, error) {
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
url_values := &url.Values{}
|
|
||||||
body_string, err := controller.decortAPICall("POST", AccountsListAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
model := AccountsListResp{}
|
|
||||||
err = json.Unmarshal([]byte(body_string), &model)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("utilityGetAccountIdByName: traversing decoded Json of length %d", len(model))
|
|
||||||
for index, item := range model {
|
|
||||||
// need to match Account by name
|
|
||||||
if item.Name == account_name {
|
|
||||||
log.Debugf("utilityGetAccountIdByName: match Account name %q / ID %d at index %d",
|
|
||||||
item.Name, item.ID, index)
|
|
||||||
return item.ID, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, fmt.Errorf("Cannot find account %q for the current user. Check account name and your access rights", account_name)
|
|
||||||
}
|
|
@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
|
||||||
Technology platfom.
|
|
||||||
|
|
||||||
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package decort
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
|
||||||
// "github.com/hashicorp/terraform/helper/validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (ctrl *ControllerCfg) utilityVmDisksProvision(mcfg *MachineConfig) error {
|
|
||||||
for index, disk := range mcfg.DataDisks {
|
|
||||||
url_values := &url.Values{}
|
|
||||||
// url_values.Add("machineId", fmt.Sprintf("%d", mcfg.ID))
|
|
||||||
url_values.Add("accountId", fmt.Sprintf("%d", mcfg.TenantID))
|
|
||||||
url_values.Add("gid", fmt.Sprintf("%d", mcfg.GridID))
|
|
||||||
url_values.Add("name", fmt.Sprintf("%s", disk.Label))
|
|
||||||
url_values.Add("description", fmt.Sprintf("Data disk for VM ID %d / VM Name: %s", mcfg.ID, mcfg.Name))
|
|
||||||
url_values.Add("size", fmt.Sprintf("%d", disk.Size))
|
|
||||||
url_values.Add("type", "D")
|
|
||||||
// url_values.Add("iops", )
|
|
||||||
|
|
||||||
disk_id_resp, err := ctrl.decortAPICall("POST", DiskCreateAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
// failed to create disk - partial resource update
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// disk created - API call returns disk ID as a string - use it to update
|
|
||||||
// disk ID in the corresponding MachineConfig.DiskConfig record
|
|
||||||
|
|
||||||
mcfg.DataDisks[index].ID, err = strconv.Atoi(disk_id_resp)
|
|
||||||
if err != nil {
|
|
||||||
// failed to convert disk ID into proper integer value - partial resource update
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// now that we have disk created and stored its ID in the mcfg.DataDisks[index].ID
|
|
||||||
// we can attempt attaching the disk to the VM
|
|
||||||
url_values = &url.Values{}
|
|
||||||
// url_values.Add("machineId", fmt.Sprintf("%d", mcfg.ID))
|
|
||||||
url_values.Add("machineId", fmt.Sprintf("%d", mcfg.ID))
|
|
||||||
url_values.Add("diskId", disk_id_resp)
|
|
||||||
_, err = ctrl.decortAPICall("POST", DiskAttachAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
// failed to attach disk - partial resource update
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (ctrl *ControllerCfg) utilityVmPortforwardsProvision(mcfg *MachineConfig) error {
|
|
||||||
for _, rule := range mcfg.PortForwards {
|
|
||||||
url_values := &url.Values{}
|
|
||||||
url_values.Add("machineId", fmt.Sprintf("%d", mcfg.ID))
|
|
||||||
url_values.Add("cloudspaceId", fmt.Sprintf("%d", mcfg.ResGroupID))
|
|
||||||
url_values.Add("publicIp", mcfg.ExtIP) // this may be obsoleted by Resource group implementation
|
|
||||||
url_values.Add("publicPort", fmt.Sprintf("%d", rule.ExtPort))
|
|
||||||
url_values.Add("localPort", fmt.Sprintf("%d", rule.IntPort))
|
|
||||||
url_values.Add("protocol", rule.Proto)
|
|
||||||
_, err := ctrl.decortAPICall("POST", PortforwardingCreateAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
// failed to create port forward rule - partial resource update
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctrl *ControllerCfg) utilityVmNetworksProvision(mcfg *MachineConfig) error {
|
|
||||||
for _, net := range mcfg.Networks {
|
|
||||||
url_values := &url.Values{}
|
|
||||||
url_values.Add("machineId", fmt.Sprintf("%d", mcfg.ID))
|
|
||||||
url_values.Add("externalNetworkId", fmt.Sprintf("%d", net.NetworkID))
|
|
||||||
_, err := ctrl.decortAPICall("POST", AttachExternalNetworkAPI, url_values)
|
|
||||||
if err != nil {
|
|
||||||
// failed to attach network - partial resource update
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func utilityVmCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
|
|
||||||
// This function tries to locate VM by its name and resource group ID
|
|
||||||
// if succeeded, it returns non empty string that contains JSON formatted facts about the VM
|
|
||||||
// as returned by machines/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.
|
|
||||||
//
|
|
||||||
name := d.Get("name").(string)
|
|
||||||
rgid := d.Get("rgid").(int)
|
|
||||||
|
|
||||||
controller := m.(*ControllerCfg)
|
|
||||||
list_url_values := &url.Values{}
|
|
||||||
list_url_values.Add("cloudspaceId", fmt.Sprintf("%d",rgid))
|
|
||||||
body_string, err := controller.decortAPICall("POST", MachinesListAPI, list_url_values)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// log.Printf("%s", body_string)
|
|
||||||
// log.Printf("dataSourceVmRead: ready to decode mashines/list response body")
|
|
||||||
vm_list := MachinesListResp{}
|
|
||||||
err = json.Unmarshal([]byte(body_string), &vm_list)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// log.Printf("%#v", vm_list)
|
|
||||||
// log.Printf("dataSourceVmRead: traversing decoded JSON of length %d", len(vm_list))
|
|
||||||
for _, item := range vm_list {
|
|
||||||
// need to match VM by name, skip VMs with the same name in DESTROYED satus
|
|
||||||
if item.Name == name && item.Status != "DESTROYED" {
|
|
||||||
// log.Printf("dataSourceVmRead: index %d, matched name %q", index, item.Name)
|
|
||||||
// we found the VM we need - not get detailed information via API call to cloudapi/machines/get
|
|
||||||
get_url_values := &url.Values{}
|
|
||||||
get_url_values.Add("machineId", fmt.Sprintf("%d", item.ID))
|
|
||||||
body_string, err = controller.decortAPICall("POST", MachinesGetAPI, get_url_values)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return body_string, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", nil // there should be no error if VM does not exist
|
|
||||||
// return "", fmt.Errorf("Cannot find VM name %q in resource group ID %d", name, rgid)
|
|
||||||
}
|
|
Loading…
Reference in new issue