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
// new CloudStack client.
type Config struct {
ApiURL string
ApiKey string
SecretKey string
Timeout int64
APIURL string
APIKey string
SecretKey string
HTTPGETOnly bool
Timeout int64
}
// Client() returns a new CloudStack client.
// NewClient returns a new CloudStack client.
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)
return cs, nil
}

View File

@ -27,6 +27,12 @@ func Provider() terraform.ResourceProvider {
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{
Type: schema.TypeInt,
Required: true,
@ -45,6 +51,7 @@ func Provider() terraform.ResourceProvider {
"cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(),
"cloudstack_nic": resourceCloudStackNIC(),
"cloudstack_port_forward": resourceCloudStackPortForward(),
"cloudstack_secondary_ipaddress": resourceCloudStackSecondaryIPAddress(),
"cloudstack_ssh_keypair": resourceCloudStackSSHKeyPair(),
"cloudstack_template": resourceCloudStackTemplate(),
"cloudstack_vpc": resourceCloudStackVPC(),
@ -59,10 +66,11 @@ func Provider() terraform.ResourceProvider {
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := Config{
ApiURL: d.Get("api_url").(string),
ApiKey: d.Get("api_key").(string),
SecretKey: d.Get("secret_key").(string),
Timeout: int64(d.Get("timeout").(int)),
APIURL: d.Get("api_url").(string),
APIKey: d.Get("api_key").(string),
SecretKey: d.Get("secret_key").(string),
HTTPGETOnly: d.Get("http_get_only").(bool),
Timeout: int64(d.Get("timeout").(int)),
}
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("virtual_machine", v.Vmname)
setValueOrUUID(d, "virtual_machine", v.Vmname, v.Virtualmachineid)
}
return nil

View File

@ -106,8 +106,14 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
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
zone, _, err := cs.Zone.GetZoneByName(d.Get("zone").(string))
zone, _, err := cs.Zone.GetZoneByID(zoneid)
if err != nil {
return err
}
@ -168,12 +174,18 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
if userData, ok := d.GetOk("user_data"); ok {
ud := base64.StdEncoding.EncodeToString([]byte(userData.(string)))
// deployVirtualMachine uses POST, so max userdata is 32K
// https://github.com/xanzy/go-cloudstack/commit/c767de689df1faedfec69233763a7c5334bee1f6
if len(ud) > 32768 {
// deployVirtualMachine uses POST by default, so max userdata is 32K
maxUD := 32768
if cs.HTTPGETOnly {
// deployVirtualMachine using GET instead, so max userdata is 2K
maxUD = 2048
}
if len(ud) > maxUD {
return fmt.Errorf(
"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)
@ -204,7 +216,6 @@ func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) er
if err != nil {
if count == 0 {
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("")
return nil
}
@ -216,13 +227,13 @@ func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) er
d.Set("name", vm.Name)
d.Set("display_name", vm.Displayname)
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
setValueOrUUID(d, "network", vm.Nic[0].Networkname, vm.Nic[0].Networkid)
setValueOrUUID(d, "service_offering", vm.Serviceofferingname, vm.Serviceofferingid)
setValueOrUUID(d, "template", vm.Templatename, vm.Templateid)
setValueOrUUID(d, "project", vm.Project, vm.Projectid)
setValueOrUUID(d, "zone", vm.Zonename, vm.Zoneid)
return nil
}
@ -256,7 +267,8 @@ func resourceCloudStackInstanceUpdate(d *schema.ResourceData, meta interface{})
// Attributes that require reboot to update
if d.HasChange("service_offering") || d.HasChange("keypair") {
// 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 {
return fmt.Errorf(
"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
_, err = cs.VirtualMachine.StartVirtualMachine(cs.VirtualMachine.NewStartVirtualMachineParams(d.Id()))
_, err = cs.VirtualMachine.StartVirtualMachine(
cs.VirtualMachine.NewStartVirtualMachineParams(d.Id()))
if err != nil {
return fmt.Errorf(
"Error starting instance %s after making changes", name)
}
}
d.Partial(false)
return resourceCloudStackInstanceRead(d, meta)
}

View File

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

View File

@ -157,21 +157,23 @@ func resourceCloudStackNetworkACLRuleCreateRule(
p.SetIcmptype(rule["icmp_type"].(int))
p.SetIcmpcode(rule["icmp_code"].(int))
r, err := cs.NetworkACL.CreateNetworkACL(p)
r, err := Retry(4, retryableACLCreationFunc(cs, p))
if err != nil {
return err
}
uuids["icmp"] = r.Id
uuids["icmp"] = r.(*cloudstack.CreateNetworkACLResponse).Id
rule["uuids"] = uuids
}
// If the protocol is ALL set the needed parameters
if rule["protocol"].(string) == "all" {
r, err := cs.NetworkACL.CreateNetworkACL(p)
r, err := Retry(4, retryableACLCreationFunc(cs, p))
if err != nil {
return err
}
uuids["all"] = r.Id
uuids["all"] = r.(*cloudstack.CreateNetworkACLResponse).Id
rule["uuids"] = uuids
}
@ -206,7 +208,7 @@ func resourceCloudStackNetworkACLRuleCreateRule(
p.SetStartport(startPort)
p.SetEndport(endPort)
r, err := cs.NetworkACL.CreateNetworkACL(p)
r, err := Retry(4, retryableACLCreationFunc(cs, p))
if err != nil {
return err
}
@ -214,7 +216,7 @@ func resourceCloudStackNetworkACLRuleCreateRule(
ports.Add(port)
rule["ports"] = ports
uuids[port.(string)] = r.Id
uuids[port.(string)] = r.(*cloudstack.CreateNetworkACLResponse).Id
rule["uuids"] = uuids
}
}
@ -593,3 +595,15 @@ func verifyNetworkACLRuleParams(d *schema.ResourceData, rule map[string]interfac
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
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 {
return err
}
@ -186,7 +191,13 @@ func resourceCloudStackPortForwardRead(d *schema.ResourceData, meta interface{})
forward["protocol"] = r.Protocol
forward["private_port"] = privPort
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)
}
}
@ -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
forward := map[string]interface{}{
"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"
"log"
"regexp"
"time"
"github.com/hashicorp/terraform/helper/schema"
"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}$`)
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,
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
`CLOUDSTACK_TIMEOUT` environment variable. Otherwise, this will default to 300
`CLOUDSTACK_TIMEOUT` environment variable. Otherwise, this will default to 300
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
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.
* `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:
* `ipaddress` - (Required) The IP address for which to create the firewall rules.
Changing this forces a new resource to be created.
* `ipaddress` - (Required) The IP address or ID for which to create the firewall
rules. Changing this forces a new resource to be created.
* `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

View File

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

View File

@ -22,10 +22,10 @@ resource "cloudstack_ipaddress" "default" {
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.
* `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.
*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
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.
* `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.
* `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

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.