2015-10-26 15:45:48 +01:00
|
|
|
package vcd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-12-21 10:54:24 +01:00
|
|
|
"log"
|
|
|
|
|
2015-10-26 15:45:48 +01:00
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
2015-11-06 11:19:59 +01:00
|
|
|
types "github.com/hmrc/vmware-govcd/types/v56"
|
2015-10-26 15:45:48 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func resourceVcdVApp() *schema.Resource {
|
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceVcdVAppCreate,
|
|
|
|
Update: resourceVcdVAppUpdate,
|
|
|
|
Read: resourceVcdVAppRead,
|
|
|
|
Delete: resourceVcdVAppDelete,
|
|
|
|
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"template_name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"catalog_name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"network_href": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"network_name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
"memory": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"cpus": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"ip": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"initscript": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
"metadata": &schema.Schema{
|
|
|
|
Type: schema.TypeMap,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"href": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
"power_on": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceVcdVAppCreate(d *schema.ResourceData, meta interface{}) error {
|
2015-11-19 00:56:17 +01:00
|
|
|
vcdClient := meta.(*VCDClient)
|
2015-10-26 15:45:48 +01:00
|
|
|
|
2015-11-16 21:11:05 +01:00
|
|
|
catalog, err := vcdClient.Org.FindCatalog(d.Get("catalog_name").(string))
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error finding catalog: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
catalogitem, err := catalog.FindCatalogItem(d.Get("template_name").(string))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error finding catelog item: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
vapptemplate, err := catalogitem.GetVAppTemplate()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error finding VAppTemplate: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] VAppTemplate: %#v", vapptemplate)
|
|
|
|
var networkHref string
|
2015-11-16 21:11:05 +01:00
|
|
|
net, err := vcdClient.OrgVdc.FindVDCNetwork(d.Get("network_name").(string))
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error finding OrgVCD Network: %#v", err)
|
|
|
|
}
|
|
|
|
if attr, ok := d.GetOk("network_href"); ok {
|
|
|
|
networkHref = attr.(string)
|
|
|
|
} else {
|
|
|
|
networkHref = net.OrgVDCNetwork.HREF
|
|
|
|
}
|
2015-11-16 21:11:05 +01:00
|
|
|
// vapptemplate := govcd.NewVAppTemplate(&vcdClient.Client)
|
2015-10-26 15:45:48 +01:00
|
|
|
//
|
|
|
|
createvapp := &types.InstantiateVAppTemplateParams{
|
|
|
|
Ovf: "http://schemas.dmtf.org/ovf/envelope/1",
|
|
|
|
Xmlns: "http://www.vmware.com/vcloud/v1.5",
|
|
|
|
Name: d.Get("name").(string),
|
|
|
|
InstantiationParams: &types.InstantiationParams{
|
|
|
|
NetworkConfigSection: &types.NetworkConfigSection{
|
|
|
|
Info: "Configuration parameters for logical networks",
|
|
|
|
NetworkConfig: &types.VAppNetworkConfiguration{
|
|
|
|
NetworkName: d.Get("network_name").(string),
|
|
|
|
Configuration: &types.NetworkConfiguration{
|
|
|
|
ParentNetwork: &types.Reference{
|
|
|
|
HREF: networkHref,
|
|
|
|
},
|
|
|
|
FenceMode: "bridged",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Source: &types.Reference{
|
|
|
|
HREF: vapptemplate.VAppTemplate.HREF,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2015-11-19 00:56:17 +01:00
|
|
|
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
|
2015-11-16 21:11:05 +01:00
|
|
|
e := vcdClient.OrgVdc.InstantiateVAppTemplate(createvapp)
|
2015-10-26 15:45:48 +01:00
|
|
|
|
2015-11-02 17:39:56 +01:00
|
|
|
if e != nil {
|
|
|
|
return fmt.Errorf("Error: %#v", e)
|
|
|
|
}
|
|
|
|
|
2015-11-16 21:11:05 +01:00
|
|
|
e = vcdClient.OrgVdc.Refresh()
|
2015-11-02 17:39:56 +01:00
|
|
|
if e != nil {
|
|
|
|
return fmt.Errorf("Error: %#v", e)
|
2015-10-26 15:45:48 +01:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-11-16 21:11:05 +01:00
|
|
|
vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Get("name").(string))
|
2015-10-26 15:45:48 +01:00
|
|
|
|
2015-11-19 00:56:17 +01:00
|
|
|
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
|
2015-11-02 17:39:56 +01:00
|
|
|
task, err := vapp.ChangeVMName(d.Get("name").(string))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error with vm name change: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return task.WaitTaskCompletion()
|
|
|
|
})
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error changing vmname: %#v", err)
|
|
|
|
}
|
|
|
|
|
2015-11-19 00:56:17 +01:00
|
|
|
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
|
2015-11-02 17:39:56 +01:00
|
|
|
task, err := vapp.ChangeNetworkConfig(d.Get("network_name").(string), d.Get("ip").(string))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error with Networking change: %#v", err)
|
|
|
|
}
|
|
|
|
return task.WaitTaskCompletion()
|
|
|
|
})
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error changing network: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if initscript, ok := d.GetOk("initscript"); ok {
|
2015-11-19 00:56:17 +01:00
|
|
|
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
|
2015-11-02 17:39:56 +01:00
|
|
|
task, err := vapp.RunCustomizationScript(d.Get("name").(string), initscript.(string))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error with setting init script: %#v", err)
|
|
|
|
}
|
|
|
|
return task.WaitTaskCompletion()
|
|
|
|
})
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error completing tasks: %#v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d.SetId(d.Get("name").(string))
|
|
|
|
|
2015-11-11 00:35:25 +01:00
|
|
|
return resourceVcdVAppUpdate(d, meta)
|
2015-10-26 15:45:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func resourceVcdVAppUpdate(d *schema.ResourceData, meta interface{}) error {
|
2015-11-19 00:56:17 +01:00
|
|
|
vcdClient := meta.(*VCDClient)
|
2015-11-16 21:11:05 +01:00
|
|
|
vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())
|
2015-10-26 15:45:48 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error finding VApp: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
status, err := vapp.GetStatus()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error getting VApp status: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("metadata") {
|
|
|
|
oraw, nraw := d.GetChange("metadata")
|
|
|
|
metadata := oraw.(map[string]interface{})
|
2015-11-16 21:11:05 +01:00
|
|
|
for k := range metadata {
|
2015-10-26 15:45:48 +01:00
|
|
|
task, err := vapp.DeleteMetadata(k)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error deleting metadata: %#v", err)
|
|
|
|
}
|
|
|
|
err = task.WaitTaskCompletion()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error completing tasks: %#v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
metadata = nraw.(map[string]interface{})
|
|
|
|
for k, v := range metadata {
|
|
|
|
task, err := vapp.AddMetadata(k, v.(string))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error adding metadata: %#v", err)
|
|
|
|
}
|
|
|
|
err = task.WaitTaskCompletion()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error completing tasks: %#v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("memory") || d.HasChange("cpus") || d.HasChange("power_on") {
|
|
|
|
if status != "POWERED_OFF" {
|
|
|
|
task, err := vapp.PowerOff()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error Powering Off: %#v", err)
|
|
|
|
}
|
|
|
|
err = task.WaitTaskCompletion()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error completing tasks: %#v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("memory") {
|
2015-11-19 00:56:17 +01:00
|
|
|
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
|
2015-11-11 00:35:25 +01:00
|
|
|
task, err := vapp.ChangeMemorySize(d.Get("memory").(int))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error changing memory size: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return task.WaitTaskCompletion()
|
|
|
|
})
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
2015-11-11 00:35:25 +01:00
|
|
|
return err
|
2015-10-26 15:45:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("cpus") {
|
2015-11-19 00:56:17 +01:00
|
|
|
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
|
2015-11-11 00:35:25 +01:00
|
|
|
task, err := vapp.ChangeCPUcount(d.Get("cpus").(int))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error changing cpu count: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return task.WaitTaskCompletion()
|
|
|
|
})
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
2015-11-11 00:35:25 +01:00
|
|
|
return fmt.Errorf("Error completing task: %#v", err)
|
2015-10-26 15:45:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.Get("power_on").(bool) {
|
|
|
|
task, err := vapp.PowerOn()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error Powering Up: %#v", err)
|
|
|
|
}
|
|
|
|
err = task.WaitTaskCompletion()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error completing tasks: %#v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return resourceVcdVAppRead(d, meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceVcdVAppRead(d *schema.ResourceData, meta interface{}) error {
|
2015-11-19 00:56:17 +01:00
|
|
|
vcdClient := meta.(*VCDClient)
|
2015-10-26 15:45:48 +01:00
|
|
|
|
2015-11-16 21:11:05 +01:00
|
|
|
err := vcdClient.OrgVdc.Refresh()
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error refreshing vdc: %#v", err)
|
|
|
|
}
|
|
|
|
|
2016-02-18 18:36:36 +01:00
|
|
|
_, err = vcdClient.OrgVdc.FindVAppByName(d.Id())
|
2015-10-26 15:45:48 +01:00
|
|
|
if err != nil {
|
2015-11-06 17:39:40 +01:00
|
|
|
log.Printf("[DEBUG] Unable to find vapp. Removing from tfstate")
|
|
|
|
d.SetId("")
|
|
|
|
return nil
|
2015-10-26 15:45:48 +01:00
|
|
|
}
|
2016-02-18 18:36:36 +01:00
|
|
|
|
|
|
|
ip, err := getVAppIPAddress(d, meta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
d.Set("ip", ip)
|
2015-10-26 15:45:48 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-02-18 18:36:36 +01:00
|
|
|
func getVAppIPAddress(d *schema.ResourceData, meta interface{}) (string, error) {
|
|
|
|
vcdClient := meta.(*VCDClient)
|
|
|
|
var ip string
|
|
|
|
|
|
|
|
err := retryCall(vcdClient.MaxRetryTimeout, func() error {
|
|
|
|
err := vcdClient.OrgVdc.Refresh()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error refreshing vdc: %#v", err)
|
|
|
|
}
|
|
|
|
vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Unable to find vapp.")
|
|
|
|
}
|
|
|
|
ip = vapp.VApp.Children.VM[0].NetworkConnectionSection.NetworkConnection.IPAddress
|
|
|
|
if ip == "" {
|
|
|
|
return fmt.Errorf("Timeout: VM did not aquire IP address")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return ip, err
|
|
|
|
}
|
|
|
|
|
2015-10-26 15:45:48 +01:00
|
|
|
func resourceVcdVAppDelete(d *schema.ResourceData, meta interface{}) error {
|
2015-11-19 00:56:17 +01:00
|
|
|
vcdClient := meta.(*VCDClient)
|
2015-11-16 21:11:05 +01:00
|
|
|
vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())
|
2015-10-26 15:45:48 +01:00
|
|
|
|
|
|
|
if err != nil {
|
2015-11-17 18:27:39 +01:00
|
|
|
return fmt.Errorf("error finding vapp: %s", err)
|
2015-10-26 15:45:48 +01:00
|
|
|
}
|
|
|
|
|
2015-11-25 13:05:59 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error getting VApp status: %#v", err)
|
|
|
|
}
|
|
|
|
|
2015-11-25 17:53:00 +01:00
|
|
|
_ = retryCall(vcdClient.MaxRetryTimeout, func() error {
|
|
|
|
task, err := vapp.Undeploy()
|
2015-11-17 18:27:39 +01:00
|
|
|
if err != nil {
|
2015-11-25 17:53:00 +01:00
|
|
|
return fmt.Errorf("Error undeploying: %#v", err)
|
2015-11-17 18:27:39 +01:00
|
|
|
}
|
2015-11-25 17:53:00 +01:00
|
|
|
|
|
|
|
return task.WaitTaskCompletion()
|
|
|
|
})
|
2015-10-26 15:45:48 +01:00
|
|
|
|
2015-11-19 00:56:17 +01:00
|
|
|
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
|
2015-11-17 18:27:39 +01:00
|
|
|
task, err := vapp.Delete()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error deleting: %#v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return task.WaitTaskCompletion()
|
|
|
|
})
|
|
|
|
|
|
|
|
return err
|
2015-10-26 15:45:48 +01:00
|
|
|
}
|