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