Updates and tweaks

This commit is contained in:
Sander van Harmelen 2015-07-16 17:40:04 +02:00
parent 7f908e0ac6
commit 28b7b53be6
18 changed files with 544 additions and 42 deletions

View File

@ -5,15 +5,17 @@ import "github.com/xanzy/go-cloudstack/cloudstack"
// Config is the configuration structure used to instantiate a // Config is the configuration structure used to instantiate a
// new CloudStack client. // new CloudStack client.
type Config struct { type Config struct {
ApiURL string APIURL string
ApiKey string APIKey string
SecretKey string SecretKey string
Timeout int64 HTTPGETOnly bool
Timeout int64
} }
// Client() returns a new CloudStack client. // NewClient returns a new CloudStack client.
func (c *Config) NewClient() (*cloudstack.CloudStackClient, error) { func (c *Config) NewClient() (*cloudstack.CloudStackClient, error) {
cs := cloudstack.NewAsyncClient(c.ApiURL, c.ApiKey, c.SecretKey, false) cs := cloudstack.NewAsyncClient(c.APIURL, c.APIKey, c.SecretKey, false)
cs.HTTPGETOnly = c.HTTPGETOnly
cs.AsyncTimeout(c.Timeout) cs.AsyncTimeout(c.Timeout)
return cs, nil return cs, nil
} }

View File

@ -27,6 +27,12 @@ func Provider() terraform.ResourceProvider {
DefaultFunc: schema.EnvDefaultFunc("CLOUDSTACK_SECRET_KEY", nil), DefaultFunc: schema.EnvDefaultFunc("CLOUDSTACK_SECRET_KEY", nil),
}, },
"http_get_only": &schema.Schema{
Type: schema.TypeBool,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("CLOUDSTACK_HTTP_GET_ONLY", false),
},
"timeout": &schema.Schema{ "timeout": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Required: true, Required: true,
@ -45,6 +51,7 @@ func Provider() terraform.ResourceProvider {
"cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(), "cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(),
"cloudstack_nic": resourceCloudStackNIC(), "cloudstack_nic": resourceCloudStackNIC(),
"cloudstack_port_forward": resourceCloudStackPortForward(), "cloudstack_port_forward": resourceCloudStackPortForward(),
"cloudstack_secondary_ipaddress": resourceCloudStackSecondaryIPAddress(),
"cloudstack_ssh_keypair": resourceCloudStackSSHKeyPair(), "cloudstack_ssh_keypair": resourceCloudStackSSHKeyPair(),
"cloudstack_template": resourceCloudStackTemplate(), "cloudstack_template": resourceCloudStackTemplate(),
"cloudstack_vpc": resourceCloudStackVPC(), "cloudstack_vpc": resourceCloudStackVPC(),
@ -59,10 +66,11 @@ func Provider() terraform.ResourceProvider {
func providerConfigure(d *schema.ResourceData) (interface{}, error) { func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := Config{ config := Config{
ApiURL: d.Get("api_url").(string), APIURL: d.Get("api_url").(string),
ApiKey: d.Get("api_key").(string), APIKey: d.Get("api_key").(string),
SecretKey: d.Get("secret_key").(string), SecretKey: d.Get("secret_key").(string),
Timeout: int64(d.Get("timeout").(int)), HTTPGETOnly: d.Get("http_get_only").(bool),
Timeout: int64(d.Get("timeout").(int)),
} }
return config.NewClient() return config.NewClient()

View File

@ -165,7 +165,7 @@ func resourceCloudStackDiskRead(d *schema.ResourceData, meta interface{}) error
} }
d.Set("device", retrieveDeviceName(v.Deviceid, c.Name)) d.Set("device", retrieveDeviceName(v.Deviceid, c.Name))
d.Set("virtual_machine", v.Vmname) setValueOrUUID(d, "virtual_machine", v.Vmname, v.Virtualmachineid)
} }
return nil return nil

View File

@ -106,8 +106,14 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
return e.Error() return e.Error()
} }
// Retrieve the zone UUID
zoneid, e := retrieveUUID(cs, "zone", d.Get("zone").(string))
if e != nil {
return e.Error()
}
// Retrieve the zone object // Retrieve the zone object
zone, _, err := cs.Zone.GetZoneByName(d.Get("zone").(string)) zone, _, err := cs.Zone.GetZoneByID(zoneid)
if err != nil { if err != nil {
return err return err
} }
@ -168,12 +174,18 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
if userData, ok := d.GetOk("user_data"); ok { if userData, ok := d.GetOk("user_data"); ok {
ud := base64.StdEncoding.EncodeToString([]byte(userData.(string))) ud := base64.StdEncoding.EncodeToString([]byte(userData.(string)))
// deployVirtualMachine uses POST, so max userdata is 32K // deployVirtualMachine uses POST by default, so max userdata is 32K
// https://github.com/xanzy/go-cloudstack/commit/c767de689df1faedfec69233763a7c5334bee1f6 maxUD := 32768
if len(ud) > 32768 {
if cs.HTTPGETOnly {
// deployVirtualMachine using GET instead, so max userdata is 2K
maxUD = 2048
}
if len(ud) > maxUD {
return fmt.Errorf( return fmt.Errorf(
"The supplied user_data contains %d bytes after encoding, "+ "The supplied user_data contains %d bytes after encoding, "+
"this exeeds the limit of 32768 bytes", len(ud)) "this exeeds the limit of %d bytes", len(ud), maxUD)
} }
p.SetUserdata(ud) p.SetUserdata(ud)
@ -204,7 +216,6 @@ func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) er
if err != nil { if err != nil {
if count == 0 { if count == 0 {
log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("name").(string)) log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("name").(string))
// Clear out all details so it's obvious the instance is gone
d.SetId("") d.SetId("")
return nil return nil
} }
@ -216,13 +227,13 @@ func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) er
d.Set("name", vm.Name) d.Set("name", vm.Name)
d.Set("display_name", vm.Displayname) d.Set("display_name", vm.Displayname)
d.Set("ipaddress", vm.Nic[0].Ipaddress) d.Set("ipaddress", vm.Nic[0].Ipaddress)
d.Set("zone", vm.Zonename)
//NB cloudstack sometimes sends back the wrong keypair name, so dont update it //NB cloudstack sometimes sends back the wrong keypair name, so dont update it
setValueOrUUID(d, "network", vm.Nic[0].Networkname, vm.Nic[0].Networkid) setValueOrUUID(d, "network", vm.Nic[0].Networkname, vm.Nic[0].Networkid)
setValueOrUUID(d, "service_offering", vm.Serviceofferingname, vm.Serviceofferingid) setValueOrUUID(d, "service_offering", vm.Serviceofferingname, vm.Serviceofferingid)
setValueOrUUID(d, "template", vm.Templatename, vm.Templateid) setValueOrUUID(d, "template", vm.Templatename, vm.Templateid)
setValueOrUUID(d, "project", vm.Project, vm.Projectid) setValueOrUUID(d, "project", vm.Project, vm.Projectid)
setValueOrUUID(d, "zone", vm.Zonename, vm.Zoneid)
return nil return nil
} }
@ -256,7 +267,8 @@ func resourceCloudStackInstanceUpdate(d *schema.ResourceData, meta interface{})
// Attributes that require reboot to update // Attributes that require reboot to update
if d.HasChange("service_offering") || d.HasChange("keypair") { if d.HasChange("service_offering") || d.HasChange("keypair") {
// Before we can actually make these changes, the virtual machine must be stopped // Before we can actually make these changes, the virtual machine must be stopped
_, err := cs.VirtualMachine.StopVirtualMachine(cs.VirtualMachine.NewStopVirtualMachineParams(d.Id())) _, err := cs.VirtualMachine.StopVirtualMachine(
cs.VirtualMachine.NewStopVirtualMachineParams(d.Id()))
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
"Error stopping instance %s before making changes: %s", name, err) "Error stopping instance %s before making changes: %s", name, err)
@ -299,12 +311,14 @@ func resourceCloudStackInstanceUpdate(d *schema.ResourceData, meta interface{})
} }
// Start the virtual machine again // Start the virtual machine again
_, err = cs.VirtualMachine.StartVirtualMachine(cs.VirtualMachine.NewStartVirtualMachineParams(d.Id())) _, err = cs.VirtualMachine.StartVirtualMachine(
cs.VirtualMachine.NewStartVirtualMachineParams(d.Id()))
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
"Error starting instance %s after making changes", name) "Error starting instance %s after making changes", name)
} }
} }
d.Partial(false) d.Partial(false)
return resourceCloudStackInstanceRead(d, meta) return resourceCloudStackInstanceRead(d, meta)
} }

View File

@ -105,7 +105,7 @@ func resourceCloudStackIPAddressRead(d *schema.ResourceData, meta interface{}) e
return err return err
} }
d.Set("network", n.Name) setValueOrUUID(d, "network", n.Name, f.Associatednetworkid)
} }
if _, ok := d.GetOk("vpc"); ok { if _, ok := d.GetOk("vpc"); ok {
@ -115,7 +115,7 @@ func resourceCloudStackIPAddressRead(d *schema.ResourceData, meta interface{}) e
return err return err
} }
d.Set("vpc", v.Name) setValueOrUUID(d, "vpc", v.Name, f.Vpcid)
} }
return nil return nil

View File

@ -157,21 +157,23 @@ func resourceCloudStackNetworkACLRuleCreateRule(
p.SetIcmptype(rule["icmp_type"].(int)) p.SetIcmptype(rule["icmp_type"].(int))
p.SetIcmpcode(rule["icmp_code"].(int)) p.SetIcmpcode(rule["icmp_code"].(int))
r, err := cs.NetworkACL.CreateNetworkACL(p) r, err := Retry(4, retryableACLCreationFunc(cs, p))
if err != nil { if err != nil {
return err return err
} }
uuids["icmp"] = r.Id
uuids["icmp"] = r.(*cloudstack.CreateNetworkACLResponse).Id
rule["uuids"] = uuids rule["uuids"] = uuids
} }
// If the protocol is ALL set the needed parameters // If the protocol is ALL set the needed parameters
if rule["protocol"].(string) == "all" { if rule["protocol"].(string) == "all" {
r, err := cs.NetworkACL.CreateNetworkACL(p) r, err := Retry(4, retryableACLCreationFunc(cs, p))
if err != nil { if err != nil {
return err return err
} }
uuids["all"] = r.Id
uuids["all"] = r.(*cloudstack.CreateNetworkACLResponse).Id
rule["uuids"] = uuids rule["uuids"] = uuids
} }
@ -206,7 +208,7 @@ func resourceCloudStackNetworkACLRuleCreateRule(
p.SetStartport(startPort) p.SetStartport(startPort)
p.SetEndport(endPort) p.SetEndport(endPort)
r, err := cs.NetworkACL.CreateNetworkACL(p) r, err := Retry(4, retryableACLCreationFunc(cs, p))
if err != nil { if err != nil {
return err return err
} }
@ -214,7 +216,7 @@ func resourceCloudStackNetworkACLRuleCreateRule(
ports.Add(port) ports.Add(port)
rule["ports"] = ports rule["ports"] = ports
uuids[port.(string)] = r.Id uuids[port.(string)] = r.(*cloudstack.CreateNetworkACLResponse).Id
rule["uuids"] = uuids rule["uuids"] = uuids
} }
} }
@ -593,3 +595,15 @@ func verifyNetworkACLRuleParams(d *schema.ResourceData, rule map[string]interfac
return nil return nil
} }
func retryableACLCreationFunc(
cs *cloudstack.CloudStackClient,
p *cloudstack.CreateNetworkACLParams) func() (interface{}, error) {
return func() (interface{}, error) {
r, err := cs.NetworkACL.CreateNetworkACL(p)
if err != nil {
return nil, err
}
return r, nil
}
}

View File

@ -116,7 +116,12 @@ func resourceCloudStackPortForwardCreateForward(
} }
// Retrieve the virtual_machine UUID // Retrieve the virtual_machine UUID
vm, _, err := cs.VirtualMachine.GetVirtualMachineByName(forward["virtual_machine"].(string)) virtualmachineid, e := retrieveUUID(cs, "virtual_machine", forward["virtual_machine"].(string))
if e != nil {
return e.Error()
}
vm, _, err := cs.VirtualMachine.GetVirtualMachineByID(virtualmachineid)
if err != nil { if err != nil {
return err return err
} }
@ -186,7 +191,13 @@ func resourceCloudStackPortForwardRead(d *schema.ResourceData, meta interface{})
forward["protocol"] = r.Protocol forward["protocol"] = r.Protocol
forward["private_port"] = privPort forward["private_port"] = privPort
forward["public_port"] = pubPort forward["public_port"] = pubPort
forward["virtual_machine"] = r.Virtualmachinename
if isUUID(forward["virtual_machine"].(string)) {
forward["virtual_machine"] = r.Virtualmachineid
} else {
forward["virtual_machine"] = r.Virtualmachinename
}
forwards.Add(forward) forwards.Add(forward)
} }
} }
@ -219,7 +230,7 @@ func resourceCloudStackPortForwardRead(d *schema.ResourceData, meta interface{})
} }
} }
for uuid, _ := range uuids { for uuid := range uuids {
// Make a dummy forward to hold the unknown UUID // Make a dummy forward to hold the unknown UUID
forward := map[string]interface{}{ forward := map[string]interface{}{
"protocol": "N/A", "protocol": "N/A",

View File

@ -0,0 +1,160 @@
package cloudstack
import (
"fmt"
"log"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/xanzy/go-cloudstack/cloudstack"
)
func resourceCloudStackSecondaryIPAddress() *schema.Resource {
return &schema.Resource{
Create: resourceCloudStackSecondaryIPAddressCreate,
Read: resourceCloudStackSecondaryIPAddressRead,
Delete: resourceCloudStackSecondaryIPAddressDelete,
Schema: map[string]*schema.Schema{
"ipaddress": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"nicid": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"virtual_machine": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}
func resourceCloudStackSecondaryIPAddressCreate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
nicid := d.Get("nicid").(string)
if nicid == "" {
// Retrieve the virtual_machine UUID
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", d.Get("virtual_machine").(string))
if e != nil {
return e.Error()
}
// Get the virtual machine details
vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(virtualmachineid)
if err != nil {
if count == 0 {
log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("virtual_machine").(string))
d.SetId("")
return nil
}
return err
}
nicid = vm.Nic[0].Id
}
// Create a new parameter struct
p := cs.Nic.NewAddIpToNicParams(nicid)
if addr := d.Get("ipaddress").(string); addr != "" {
p.SetIpaddress(addr)
}
ip, err := cs.Nic.AddIpToNic(p)
if err != nil {
return err
}
d.SetId(ip.Id)
return nil
}
func resourceCloudStackSecondaryIPAddressRead(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
// Retrieve the virtual_machine UUID
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", d.Get("virtual_machine").(string))
if e != nil {
return e.Error()
}
// Get the virtual machine details
vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(virtualmachineid)
if err != nil {
if count == 0 {
log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("virtual_machine").(string))
d.SetId("")
return nil
}
return err
}
nicid := d.Get("nicid").(string)
if nicid == "" {
nicid = vm.Nic[0].Id
}
p := cs.Nic.NewListNicsParams(virtualmachineid)
p.SetNicid(nicid)
l, err := cs.Nic.ListNics(p)
if err != nil {
return err
}
if l.Count == 0 {
log.Printf("[DEBUG] NIC %s does no longer exist", d.Get("nicid").(string))
d.SetId("")
return nil
}
if l.Count > 1 {
return fmt.Errorf("Found more then one possible result: %v", l.Nics)
}
for _, ip := range l.Nics[0].Secondaryip {
if ip.Id == d.Id() {
d.Set("ipaddress", ip.Ipaddress)
d.Set("nicid", l.Nics[0].Id)
return nil
}
}
log.Printf("[DEBUG] IP %s no longer exist", d.Get("ipaddress").(string))
d.SetId("")
return nil
}
func resourceCloudStackSecondaryIPAddressDelete(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
// Create a new parameter struct
p := cs.Nic.NewRemoveIpFromNicParams(d.Id())
log.Printf("[INFO] Removing secondary IP address: %s", d.Get("ipaddress").(string))
if _, err := cs.Nic.RemoveIpFromNic(p); err != nil {
// This is a very poor way to be told the UUID does no longer exist :(
if strings.Contains(err.Error(), fmt.Sprintf(
"Invalid parameter id value=%s due to incorrect long value format, "+
"or entity does not exist", d.Id())) {
return nil
}
return fmt.Errorf("Error removing secondary IP address: %s", err)
}
return nil
}

View File

@ -0,0 +1,225 @@
package cloudstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/xanzy/go-cloudstack/cloudstack"
)
func TestAccCloudStackSecondaryIPAddress_basic(t *testing.T) {
var ip cloudstack.AddIpToNicResponse
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudStackSecondaryIPAddressDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCloudStackSecondaryIPAddress_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackSecondaryIPAddressExists(
"cloudstack_secondary_ipaddress.foo", &ip),
),
},
},
})
}
func TestAccCloudStackSecondaryIPAddress_fixedIP(t *testing.T) {
var ip cloudstack.AddIpToNicResponse
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudStackSecondaryIPAddressDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCloudStackSecondaryIPAddress_fixedIP,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackSecondaryIPAddressExists(
"cloudstack_secondary_ipaddress.foo", &ip),
testAccCheckCloudStackSecondaryIPAddressAttributes(&ip),
resource.TestCheckResourceAttr(
"cloudstack_secondary_ipaddress.foo", "ipaddress", CLOUDSTACK_NETWORK_1_IPADDRESS),
),
},
},
})
}
func testAccCheckCloudStackSecondaryIPAddressExists(
n string, ip *cloudstack.AddIpToNicResponse) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No IP address ID is set")
}
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
// Retrieve the virtual_machine UUID
virtualmachineid, e := retrieveUUID(
cs, "virtual_machine", rs.Primary.Attributes["virtual_machine"])
if e != nil {
return e.Error()
}
// Get the virtual machine details
vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(virtualmachineid)
if err != nil {
if count == 0 {
return fmt.Errorf("Instance not found")
}
return err
}
nicid := rs.Primary.Attributes["nicid"]
if nicid == "" {
nicid = vm.Nic[0].Id
}
p := cs.Nic.NewListNicsParams(virtualmachineid)
p.SetNicid(nicid)
l, err := cs.Nic.ListNics(p)
if err != nil {
return err
}
if l.Count == 0 {
return fmt.Errorf("NIC not found")
}
if l.Count > 1 {
return fmt.Errorf("Found more then one possible result: %v", l.Nics)
}
for _, sip := range l.Nics[0].Secondaryip {
if sip.Id == rs.Primary.ID {
ip.Ipaddress = sip.Ipaddress
ip.Nicid = l.Nics[0].Id
return nil
}
}
return fmt.Errorf("IP address not found")
}
}
func testAccCheckCloudStackSecondaryIPAddressAttributes(
ip *cloudstack.AddIpToNicResponse) resource.TestCheckFunc {
return func(s *terraform.State) error {
if ip.Ipaddress != CLOUDSTACK_NETWORK_1_IPADDRESS {
return fmt.Errorf("Bad IP address: %s", ip.Ipaddress)
}
return nil
}
}
func testAccCheckCloudStackSecondaryIPAddressDestroy(s *terraform.State) error {
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
for _, rs := range s.RootModule().Resources {
if rs.Type != "cloudstack_secondary_ipaddress" {
continue
}
if rs.Primary.ID == "" {
return fmt.Errorf("No IP address ID is set")
}
// Retrieve the virtual_machine UUID
virtualmachineid, e := retrieveUUID(
cs, "virtual_machine", rs.Primary.Attributes["virtual_machine"])
if e != nil {
return e.Error()
}
// Get the virtual machine details
vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(virtualmachineid)
if err != nil {
if count == 0 {
return fmt.Errorf("Instance not found")
}
return err
}
nicid := rs.Primary.Attributes["nicid"]
if nicid == "" {
nicid = vm.Nic[0].Id
}
p := cs.Nic.NewListNicsParams(virtualmachineid)
p.SetNicid(nicid)
l, err := cs.Nic.ListNics(p)
if err != nil {
return err
}
if l.Count == 0 {
return fmt.Errorf("NIC not found")
}
if l.Count > 1 {
return fmt.Errorf("Found more then one possible result: %v", l.Nics)
}
for _, sip := range l.Nics[0].Secondaryip {
if sip.Id == rs.Primary.ID {
return fmt.Errorf("IP address %s still exists", rs.Primary.ID)
}
}
return nil
}
return nil
}
var testAccCloudStackSecondaryIPAddress_basic = fmt.Sprintf(`
resource "cloudstack_instance" "foobar" {
name = "terraform-test"
service_offering= "%s"
network = "%s"
template = "%s"
zone = "%s"
expunge = true
}
resource "cloudstack_secondary_ipaddress" "foo" {
virtual_machine = "${cloudstack_instance.foobar.id}"
}
`,
CLOUDSTACK_SERVICE_OFFERING_1,
CLOUDSTACK_NETWORK_1,
CLOUDSTACK_TEMPLATE,
CLOUDSTACK_ZONE)
var testAccCloudStackSecondaryIPAddress_fixedIP = fmt.Sprintf(`
resource "cloudstack_instance" "foobar" {
name = "terraform-test"
service_offering= "%s"
network = "%s"
template = "%s"
zone = "%s"
expunge = true
}
resource "cloudstack_secondary_ipaddress" "foo" {
ipaddress = "%s"
virtual_machine = "${cloudstack_instance.foobar.id}"
}`,
CLOUDSTACK_SERVICE_OFFERING_1,
CLOUDSTACK_NETWORK_1,
CLOUDSTACK_TEMPLATE,
CLOUDSTACK_ZONE,
CLOUDSTACK_NETWORK_1_IPADDRESS)

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"regexp" "regexp"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/xanzy/go-cloudstack/cloudstack" "github.com/xanzy/go-cloudstack/cloudstack"
@ -113,3 +114,24 @@ func isUUID(s string) bool {
re := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) re := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
return re.MatchString(s) return re.MatchString(s)
} }
// RetryFunc is the function retried n times
type RetryFunc func() (interface{}, error)
// Retry is a wrapper around a RetryFunc that will retry a function
// n times or until it succeeds.
func Retry(n int, f RetryFunc) (interface{}, error) {
var lastErr error
for i := 0; i < n; i++ {
r, err := f()
if err == nil {
return r, nil
}
lastErr = err
time.Sleep(30 * time.Second)
}
return nil, lastErr
}

View File

@ -44,7 +44,12 @@ The following arguments are supported:
* `secret_key` - (Required) This is the CloudStack secret key. It must be provided, * `secret_key` - (Required) This is the CloudStack secret key. It must be provided,
but it can also be sourced from the `CLOUDSTACK_SECRET_KEY` environment variable. but it can also be sourced from the `CLOUDSTACK_SECRET_KEY` environment variable.
* `timeout` - (Optional) A value in seconds. This is the time allowed for Cloudstack * `http_get_only` - (Optional) Some cloud providers only allow HTTP GET calls to
their CloudStack API. If using such a provider, you need to set this to `true`
in order for the provider to only make GET calls and no POST calls. It can also
be sourced from the `CLOUDSTACK_HTTP_GET_ONLY` environment variable.
* `timeout` - (Optional) A value in seconds. This is the time allowed for Cloudstack
to complete each asynchronous job triggered. If unset, this can be sourced from the to complete each asynchronous job triggered. If unset, this can be sourced from the
`CLOUDSTACK_TIMEOUT` environment variable. Otherwise, this will default to 300 `CLOUDSTACK_TIMEOUT` environment variable. Otherwise, this will default to 300
seconds. seconds.

View File

@ -44,7 +44,7 @@ The following arguments are supported:
* `shrink_ok` - (Optional) Verifies if the disk volume is allowed to shrink when * `shrink_ok` - (Optional) Verifies if the disk volume is allowed to shrink when
resizing (defaults false). resizing (defaults false).
* `virtual_machine` - (Optional) The name of the virtual machine to which you * `virtual_machine` - (Optional) The name or ID of the virtual machine to which you
want to attach the disk volume. want to attach the disk volume.
* `zone` - (Required) The name or ID of the zone where this disk volume will be available. * `zone` - (Required) The name or ID of the zone where this disk volume will be available.

View File

@ -28,8 +28,8 @@ resource "cloudstack_firewall" "default" {
The following arguments are supported: The following arguments are supported:
* `ipaddress` - (Required) The IP address for which to create the firewall rules. * `ipaddress` - (Required) The IP address or ID for which to create the firewall
Changing this forces a new resource to be created. rules. Changing this forces a new resource to be created.
* `managed` - (Optional) USE WITH CAUTION! If enabled all the firewall rules for * `managed` - (Optional) USE WITH CAUTION! If enabled all the firewall rules for
this IP address will be managed by this resource. This means it will delete this IP address will be managed by this resource. This means it will delete

View File

@ -47,8 +47,8 @@ The following arguments are supported:
* `project` - (Optional) The name or ID of the project to deploy this * `project` - (Optional) The name or ID of the project to deploy this
instance to. Changing this forces a new resource to be created. instance to. Changing this forces a new resource to be created.
* `zone` - (Required) The name of the zone where this instance will be created. * `zone` - (Required) The name or ID of the zone where this instance will be
Changing this forces a new resource to be created. created. Changing this forces a new resource to be created.
* `user_data` - (Optional) The user data to provide when launching the * `user_data` - (Optional) The user data to provide when launching the
instance. instance.

View File

@ -22,10 +22,10 @@ resource "cloudstack_ipaddress" "default" {
The following arguments are supported: The following arguments are supported:
* `network` - (Optional) The name of the network for which an IP address should * `network` - (Optional) The name or ID of the network for which an IP address should
be acquired and associated. Changing this forces a new resource to be created. be acquired and associated. Changing this forces a new resource to be created.
* `vpc` - (Optional) The name of the VPC for which an IP address should * `vpc` - (Optional) The name or ID of the VPC for which an IP address should
be acquired and associated. Changing this forces a new resource to be created. be acquired and associated. Changing this forces a new resource to be created.
*NOTE: Either `network` or `vpc` should have a value!* *NOTE: Either `network` or `vpc` should have a value!*

View File

@ -37,7 +37,7 @@ The following arguments are supported:
* `network_offering` - (Required) The name or ID of the network offering to use * `network_offering` - (Required) The name or ID of the network offering to use
for this network. for this network.
* `vpc` - (Optional) The name of the VPC to create this network for. Changing * `vpc` - (Optional) The name or ID of the VPC to create this network for. Changing
this forces a new resource to be created. this forces a new resource to be created.
* `aclid` - (Optional) The ID of a network ACL that should be attached to the * `aclid` - (Optional) The ID of a network ACL that should be attached to the

View File

@ -48,7 +48,7 @@ The `forward` block supports:
* `public_port` - (Required) The public port to forward from. * `public_port` - (Required) The public port to forward from.
* `virtual_machine` - (Required) The name of the virtual machine to forward to. * `virtual_machine` - (Required) The name or ID of the virtual machine to forward to.
## Attributes Reference ## Attributes Reference

View File

@ -0,0 +1,41 @@
---
layout: "cloudstack"
page_title: "CloudStack: cloudstack_secondary_ipaddress"
sidebar_current: "docs-cloudstack-resource-secondary-ipaddress"
description: |-
Assigns a secondary IP to a NIC.
---
# cloudstack\_secondary\_ipaddress
Assigns a secondary IP to a NIC.
## Example Usage
```
resource "cloudstack_secondary_ipaddress" "default" {
virtual_machine = "server-1"
}
```
## Argument Reference
The following arguments are supported:
* `ipaddress` - (Optional) The IP address to attach the to NIC. If not supplied
an IP address will be selected randomly. Changing this forces a new resource
to be created.
* `nicid` - (Optional) The ID of the NIC to which you want to attach the
secondary IP address. Changing this forces a new resource to be
created (defaults to the ID of the primary NIC)
* `virtual_machine` - (Required) The name or ID of the virtual machine to which
you want to attach the secondary IP address. Changing this forces a new
resource to be created.
## Attributes Reference
The following attributes are exported:
* `id` - The secondary IP address ID.