Merge pull request #924 from jrperritt/openstack-gophercloud-v1.0

OpenStack Provider
This commit is contained in:
Paul Hinze 2015-03-31 16:58:21 -05:00
commit 08814a51ba
52 changed files with 7550 additions and 1 deletions

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/hashicorp/terraform/builtin/providers/openstack"
"github.com/hashicorp/terraform/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: openstack.Provider,
})
}

View File

@ -0,0 +1,67 @@
package openstack
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
)
type Config struct {
Username string
UserID string
Password string
APIKey string
IdentityEndpoint string
TenantID string
TenantName string
DomainID string
DomainName string
osClient *gophercloud.ProviderClient
}
func (c *Config) loadAndValidate() error {
ao := gophercloud.AuthOptions{
Username: c.Username,
UserID: c.UserID,
Password: c.Password,
APIKey: c.APIKey,
IdentityEndpoint: c.IdentityEndpoint,
TenantID: c.TenantID,
TenantName: c.TenantName,
DomainID: c.DomainID,
DomainName: c.DomainName,
}
client, err := openstack.AuthenticatedClient(ao)
if err != nil {
return err
}
c.osClient = client
return nil
}
func (c *Config) blockStorageV1Client(region string) (*gophercloud.ServiceClient, error) {
return openstack.NewBlockStorageV1(c.osClient, gophercloud.EndpointOpts{
Region: region,
})
}
func (c *Config) computeV2Client(region string) (*gophercloud.ServiceClient, error) {
return openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{
Region: region,
})
}
func (c *Config) networkingV2Client(region string) (*gophercloud.ServiceClient, error) {
return openstack.NewNetworkV2(c.osClient, gophercloud.EndpointOpts{
Region: region,
})
}
func (c *Config) objectStorageV1Client(region string) (*gophercloud.ServiceClient, error) {
return openstack.NewObjectStorageV1(c.osClient, gophercloud.EndpointOpts{
Region: region,
})
}

View File

@ -0,0 +1,113 @@
package openstack
import (
"os"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
// Provider returns a schema.Provider for OpenStack.
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"auth_url": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: envDefaultFunc("OS_AUTH_URL"),
},
"user_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: envDefaultFunc("OS_USERNAME"),
},
"user_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "",
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "",
},
"tenant_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: envDefaultFunc("OS_TENANT_NAME"),
},
"password": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: envDefaultFunc("OS_PASSWORD"),
},
"api_key": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "",
},
"domain_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "",
},
"domain_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "",
},
},
ResourcesMap: map[string]*schema.Resource{
"openstack_blockstorage_volume_v1": resourceBlockStorageVolumeV1(),
"openstack_compute_instance_v2": resourceComputeInstanceV2(),
"openstack_compute_keypair_v2": resourceComputeKeypairV2(),
"openstack_compute_secgroup_v2": resourceComputeSecGroupV2(),
"openstack_compute_floatingip_v2": resourceComputeFloatingIPV2(),
"openstack_fw_firewall_v1": resourceFWFirewallV1(),
"openstack_fw_policy_v1": resourceFWPolicyV1(),
"openstack_fw_rule_v1": resourceFWRuleV1(),
"openstack_lb_monitor_v1": resourceLBMonitorV1(),
"openstack_lb_pool_v1": resourceLBPoolV1(),
"openstack_lb_vip_v1": resourceLBVipV1(),
"openstack_networking_network_v2": resourceNetworkingNetworkV2(),
"openstack_networking_subnet_v2": resourceNetworkingSubnetV2(),
"openstack_networking_floatingip_v2": resourceNetworkingFloatingIPV2(),
"openstack_networking_router_v2": resourceNetworkingRouterV2(),
"openstack_networking_router_interface_v2": resourceNetworkingRouterInterfaceV2(),
"openstack_objectstorage_container_v1": resourceObjectStorageContainerV1(),
},
ConfigureFunc: configureProvider,
}
}
func configureProvider(d *schema.ResourceData) (interface{}, error) {
config := Config{
IdentityEndpoint: d.Get("auth_url").(string),
Username: d.Get("user_name").(string),
UserID: d.Get("user_id").(string),
Password: d.Get("password").(string),
APIKey: d.Get("api_key").(string),
TenantID: d.Get("tenant_id").(string),
TenantName: d.Get("tenant_name").(string),
DomainID: d.Get("domain_id").(string),
DomainName: d.Get("domain_name").(string),
}
if err := config.loadAndValidate(); err != nil {
return nil, err
}
return &config, nil
}
func envDefaultFunc(k string) schema.SchemaDefaultFunc {
return func() (interface{}, error) {
if v := os.Getenv(k); v != "" {
return v, nil
}
return nil, nil
}
}

View File

@ -0,0 +1,66 @@
package openstack
import (
"os"
"testing"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
var (
OS_REGION_NAME = ""
OS_POOL_NAME = ""
)
var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider
func init() {
testAccProvider = Provider().(*schema.Provider)
testAccProviders = map[string]terraform.ResourceProvider{
"openstack": testAccProvider,
}
}
func TestProvider(t *testing.T) {
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvider_impl(t *testing.T) {
var _ terraform.ResourceProvider = Provider()
}
func testAccPreCheck(t *testing.T) {
v := os.Getenv("OS_AUTH_URL")
if v == "" {
t.Fatal("OS_AUTH_URL must be set for acceptance tests")
}
v = os.Getenv("OS_REGION_NAME")
if v == "" {
t.Fatal("OS_REGION_NAME must be set for acceptance tests")
}
OS_REGION_NAME = v
v1 := os.Getenv("OS_IMAGE_ID")
v2 := os.Getenv("OS_IMAGE_NAME")
if v1 == "" && v2 == "" {
t.Fatal("OS_IMAGE_ID or OS_IMAGE_NAME must be set for acceptance tests")
}
v = os.Getenv("OS_POOL_NAME")
if v == "" {
t.Fatal("OS_POOL_NAME must be set for acceptance tests")
}
OS_POOL_NAME = v
v1 = os.Getenv("OS_FLAVOR_ID")
v2 = os.Getenv("OS_FLAVOR_NAME")
if v1 == "" && v2 == "" {
t.Fatal("OS_FLAVOR_ID or OS_FLAVOR_NAME must be set for acceptance tests")
}
}

View File

@ -0,0 +1,314 @@
package openstack
import (
"bytes"
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
)
func resourceBlockStorageVolumeV1() *schema.Resource {
return &schema.Resource{
Create: resourceBlockStorageVolumeV1Create,
Read: resourceBlockStorageVolumeV1Read,
Update: resourceBlockStorageVolumeV1Update,
Delete: resourceBlockStorageVolumeV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"size": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"metadata": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
ForceNew: false,
},
"snapshot_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"source_vol_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"image_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"volume_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"attachment": &schema.Schema{
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"instance_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"device": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
},
Set: resourceVolumeAttachmentHash,
},
},
}
}
func resourceBlockStorageVolumeV1Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
blockStorageClient, err := config.blockStorageV1Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
}
createOpts := &volumes.CreateOpts{
Description: d.Get("description").(string),
Name: d.Get("name").(string),
Size: d.Get("size").(int),
SnapshotID: d.Get("snapshot_id").(string),
SourceVolID: d.Get("source_vol_id").(string),
ImageID: d.Get("image_id").(string),
VolumeType: d.Get("volume_type").(string),
Metadata: resourceContainerMetadataV2(d),
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
v, err := volumes.Create(blockStorageClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack volume: %s", err)
}
log.Printf("[INFO] Volume ID: %s", v.ID)
// Store the ID now
d.SetId(v.ID)
// Wait for the volume to become available.
log.Printf(
"[DEBUG] Waiting for volume (%s) to become available",
v.ID)
stateConf := &resource.StateChangeConf{
Target: "available",
Refresh: VolumeV1StateRefreshFunc(blockStorageClient, v.ID),
Timeout: 10 * time.Minute,
Delay: 10 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf(
"Error waiting for volume (%s) to become ready: %s",
v.ID, err)
}
return resourceBlockStorageVolumeV1Read(d, meta)
}
func resourceBlockStorageVolumeV1Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
blockStorageClient, err := config.blockStorageV1Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
}
v, err := volumes.Get(blockStorageClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "volume")
}
log.Printf("[DEBUG] Retreived volume %s: %+v", d.Id(), v)
d.Set("size", v.Size)
d.Set("description", v.Description)
d.Set("name", v.Name)
d.Set("snapshot_id", v.SnapshotID)
d.Set("source_vol_id", v.SourceVolID)
d.Set("volume_type", v.VolumeType)
d.Set("metadata", v.Metadata)
if len(v.Attachments) > 0 {
attachments := make([]map[string]interface{}, len(v.Attachments))
for i, attachment := range v.Attachments {
attachments[i] = make(map[string]interface{})
attachments[i]["id"] = attachment["id"]
attachments[i]["instance_id"] = attachment["server_id"]
attachments[i]["device"] = attachment["device"]
log.Printf("[DEBUG] attachment: %v", attachment)
}
d.Set("attachment", attachments)
}
return nil
}
func resourceBlockStorageVolumeV1Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
blockStorageClient, err := config.blockStorageV1Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
}
updateOpts := volumes.UpdateOpts{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
}
if d.HasChange("metadata") {
updateOpts.Metadata = resourceVolumeMetadataV1(d)
}
_, err = volumes.Update(blockStorageClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack volume: %s", err)
}
return resourceBlockStorageVolumeV1Read(d, meta)
}
func resourceBlockStorageVolumeV1Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
blockStorageClient, err := config.blockStorageV1Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
}
v, err := volumes.Get(blockStorageClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "volume")
}
// make sure this volume is detached from all instances before deleting
if len(v.Attachments) > 0 {
log.Printf("[DEBUG] detaching volumes")
if computeClient, err := config.computeV2Client(d.Get("region").(string)); err != nil {
return err
} else {
for _, volumeAttachment := range v.Attachments {
log.Printf("[DEBUG] Attachment: %v", volumeAttachment)
if err := volumeattach.Delete(computeClient, volumeAttachment["server_id"].(string), volumeAttachment["id"].(string)).ExtractErr(); err != nil {
return err
}
}
stateConf := &resource.StateChangeConf{
Pending: []string{"in-use", "attaching"},
Target: "available",
Refresh: VolumeV1StateRefreshFunc(blockStorageClient, d.Id()),
Timeout: 10 * time.Minute,
Delay: 10 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf(
"Error waiting for volume (%s) to become available: %s",
d.Id(), err)
}
}
}
err = volumes.Delete(blockStorageClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack volume: %s", err)
}
// Wait for the volume to delete before moving on.
log.Printf("[DEBUG] Waiting for volume (%s) to delete", d.Id())
stateConf := &resource.StateChangeConf{
Pending: []string{"deleting", "available"},
Target: "deleted",
Refresh: VolumeV1StateRefreshFunc(blockStorageClient, d.Id()),
Timeout: 10 * time.Minute,
Delay: 10 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf(
"Error waiting for volume (%s) to delete: %s",
d.Id(), err)
}
d.SetId("")
return nil
}
func resourceVolumeMetadataV1(d *schema.ResourceData) map[string]string {
m := make(map[string]string)
for key, val := range d.Get("metadata").(map[string]interface{}) {
m[key] = val.(string)
}
return m
}
// VolumeV1StateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an OpenStack volume.
func VolumeV1StateRefreshFunc(client *gophercloud.ServiceClient, volumeID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
v, err := volumes.Get(client, volumeID).Extract()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return nil, "", err
}
if errCode.Actual == 404 {
return v, "deleted", nil
}
return nil, "", err
}
return v, v.Status, nil
}
}
func resourceVolumeAttachmentHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
if m["instance_id"] != nil {
buf.WriteString(fmt.Sprintf("%s-", m["instance_id"].(string)))
}
return hashcode.String(buf.String())
}

View File

@ -0,0 +1,138 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
)
func TestAccBlockStorageV1Volume_basic(t *testing.T) {
var volume volumes.Volume
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBlockStorageV1VolumeDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccBlockStorageV1Volume_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckBlockStorageV1VolumeExists(t, "openstack_blockstorage_volume_v1.volume_1", &volume),
resource.TestCheckResourceAttr("openstack_blockstorage_volume_v1.volume_1", "name", "tf-test-volume"),
testAccCheckBlockStorageV1VolumeMetadata(&volume, "foo", "bar"),
),
},
resource.TestStep{
Config: testAccBlockStorageV1Volume_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_blockstorage_volume_v1.volume_1", "name", "tf-test-volume-updated"),
testAccCheckBlockStorageV1VolumeMetadata(&volume, "foo", "bar"),
),
},
},
})
}
func testAccCheckBlockStorageV1VolumeDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
blockStorageClient, err := config.blockStorageV1Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_blockstorage_volume_v1" {
continue
}
_, err := volumes.Get(blockStorageClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Volume still exists")
}
}
return nil
}
func testAccCheckBlockStorageV1VolumeExists(t *testing.T, n string, volume *volumes.Volume) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
blockStorageClient, err := config.blockStorageV1Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
}
found, err := volumes.Get(blockStorageClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Volume not found")
}
*volume = *found
return nil
}
}
func testAccCheckBlockStorageV1VolumeMetadata(
volume *volumes.Volume, k string, v string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if volume.Metadata == nil {
return fmt.Errorf("No metadata")
}
for key, value := range volume.Metadata {
if k != key {
continue
}
if v == value {
return nil
}
return fmt.Errorf("Bad value for %s: %s", k, value)
}
return fmt.Errorf("Metadata not found: %s", k)
}
}
var testAccBlockStorageV1Volume_basic = fmt.Sprintf(`
resource "openstack_blockstorage_volume_v1" "volume_1" {
region = "%s"
name = "tf-test-volume"
description = "first test volume"
metadata{
foo = "bar"
}
size = 1
}`,
OS_REGION_NAME)
var testAccBlockStorageV1Volume_update = fmt.Sprintf(`
resource "openstack_blockstorage_volume_v1" "volume_1" {
region = "%s"
name = "tf-test-volume-updated"
description = "first test volume"
metadata{
foo = "bar"
}
size = 1
}`,
OS_REGION_NAME)

View File

@ -0,0 +1,107 @@
package openstack
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
)
func resourceComputeFloatingIPV2() *schema.Resource {
return &schema.Resource{
Create: resourceComputeFloatingIPV2Create,
Read: resourceComputeFloatingIPV2Read,
Update: nil,
Delete: resourceComputeFloatingIPV2Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"pool": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_POOL_NAME"),
},
"address": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"fixed_ip": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"instance_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceComputeFloatingIPV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
createOpts := &floatingip.CreateOpts{
Pool: d.Get("pool").(string),
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
newFip, err := floatingip.Create(computeClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating Floating IP: %s", err)
}
d.SetId(newFip.ID)
return resourceComputeFloatingIPV2Read(d, meta)
}
func resourceComputeFloatingIPV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
fip, err := floatingip.Get(computeClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "floating ip")
}
log.Printf("[DEBUG] Retrieved Floating IP %s: %+v", d.Id(), fip)
d.Set("pool", fip.Pool)
d.Set("instance_id", fip.InstanceID)
d.Set("address", fip.IP)
d.Set("fixed_ip", fip.FixedIP)
return nil
}
func resourceComputeFloatingIPV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
log.Printf("[DEBUG] Deleting Floating IP %s", d.Id())
if err := floatingip.Delete(computeClient, d.Id()).ExtractErr(); err != nil {
return fmt.Errorf("Error deleting Floating IP: %s", err)
}
return nil
}

View File

@ -0,0 +1,86 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
)
func TestAccComputeV2FloatingIP_basic(t *testing.T) {
var floatingIP floatingip.FloatingIP
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeV2FloatingIPDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeV2FloatingIP_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2FloatingIPExists(t, "openstack_compute_floatingip_v2.foo", &floatingIP),
),
},
},
})
}
func testAccCheckComputeV2FloatingIPDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckComputeV2FloatingIPDestroy) Error creating OpenStack compute client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_compute_floatingip_v2" {
continue
}
_, err := floatingip.Get(computeClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("FloatingIP still exists")
}
}
return nil
}
func testAccCheckComputeV2FloatingIPExists(t *testing.T, n string, kp *floatingip.FloatingIP) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckComputeV2FloatingIPExists) Error creating OpenStack compute client: %s", err)
}
found, err := floatingip.Get(computeClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("FloatingIP not found")
}
*kp = *found
return nil
}
}
var testAccComputeV2FloatingIP_basic = fmt.Sprintf(`
resource "openstack_compute_floatingip_v2" "foo" {
}`)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,185 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/pagination"
)
func TestAccComputeV2Instance_basic(t *testing.T) {
var instance servers.Server
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeV2Instance_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
),
},
},
})
}
func TestAccComputeV2Instance_volumeAttach(t *testing.T) {
var instance servers.Server
var volume volumes.Volume
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeV2Instance_volumeAttach,
Check: resource.ComposeTestCheckFunc(
testAccCheckBlockStorageV1VolumeExists(t, "openstack_blockstorage_volume_v1.myvol", &volume),
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
testAccCheckComputeV2InstanceVolumeAttachment(&instance, &volume),
),
},
},
})
}
func testAccCheckComputeV2InstanceDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckComputeV2InstanceDestroy) Error creating OpenStack compute client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_compute_instance_v2" {
continue
}
_, err := servers.Get(computeClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Instance still exists")
}
}
return nil
}
func testAccCheckComputeV2InstanceExists(t *testing.T, n string, instance *servers.Server) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckComputeV2InstanceExists) Error creating OpenStack compute client: %s", err)
}
found, err := servers.Get(computeClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Instance not found")
}
*instance = *found
return nil
}
}
func testAccCheckComputeV2InstanceMetadata(
instance *servers.Server, k string, v string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if instance.Metadata == nil {
return fmt.Errorf("No metadata")
}
for key, value := range instance.Metadata {
if k != key {
continue
}
if v == value.(string) {
return nil
}
return fmt.Errorf("Bad value for %s: %s", k, value)
}
return fmt.Errorf("Metadata not found: %s", k)
}
}
func testAccCheckComputeV2InstanceVolumeAttachment(
instance *servers.Server, volume *volumes.Volume) resource.TestCheckFunc {
return func(s *terraform.State) error {
var attachments []volumeattach.VolumeAttachment
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return err
}
err = volumeattach.List(computeClient, instance.ID).EachPage(func(page pagination.Page) (bool, error) {
actual, err := volumeattach.ExtractVolumeAttachments(page)
if err != nil {
return false, fmt.Errorf("Unable to lookup attachment: %s", err)
}
attachments = actual
return true, nil
})
for _, attachment := range attachments {
if attachment.VolumeID == volume.ID {
return nil
}
}
return fmt.Errorf("Volume not found: %s", volume.ID)
}
}
var testAccComputeV2Instance_basic = fmt.Sprintf(`
resource "openstack_compute_instance_v2" "foo" {
region = "%s"
name = "terraform-test"
metadata {
foo = "bar"
}
}`,
OS_REGION_NAME)
var testAccComputeV2Instance_volumeAttach = fmt.Sprintf(`
resource "openstack_blockstorage_volume_v1" "myvol" {
name = "myvol"
size = 1
}
resource "openstack_compute_instance_v2" "foo" {
region = "%s"
name = "terraform-test"
volume {
volume_id = "${openstack_blockstorage_volume_v1.myvol.id}"
}
}`,
OS_REGION_NAME)

View File

@ -0,0 +1,92 @@
package openstack
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
)
func resourceComputeKeypairV2() *schema.Resource {
return &schema.Resource{
Create: resourceComputeKeypairV2Create,
Read: resourceComputeKeypairV2Read,
Delete: resourceComputeKeypairV2Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"public_key": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceComputeKeypairV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
createOpts := keypairs.CreateOpts{
Name: d.Get("name").(string),
PublicKey: d.Get("public_key").(string),
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
kp, err := keypairs.Create(computeClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack keypair: %s", err)
}
d.SetId(kp.Name)
return resourceComputeKeypairV2Read(d, meta)
}
func resourceComputeKeypairV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
kp, err := keypairs.Get(computeClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "keypair")
}
d.Set("name", kp.Name)
d.Set("public_key", kp.PublicKey)
return nil
}
func resourceComputeKeypairV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
err = keypairs.Delete(computeClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack keypair: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,90 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
)
func TestAccComputeV2Keypair_basic(t *testing.T) {
var keypair keypairs.KeyPair
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeV2KeypairDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeV2Keypair_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2KeypairExists(t, "openstack_compute_keypair_v2.foo", &keypair),
),
},
},
})
}
func testAccCheckComputeV2KeypairDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckComputeV2KeypairDestroy) Error creating OpenStack compute client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_compute_keypair_v2" {
continue
}
_, err := keypairs.Get(computeClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Keypair still exists")
}
}
return nil
}
func testAccCheckComputeV2KeypairExists(t *testing.T, n string, kp *keypairs.KeyPair) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckComputeV2KeypairExists) Error creating OpenStack compute client: %s", err)
}
found, err := keypairs.Get(computeClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.Name != rs.Primary.ID {
return fmt.Errorf("Keypair not found")
}
*kp = *found
return nil
}
}
var testAccComputeV2Keypair_basic = fmt.Sprintf(`
resource "openstack_compute_keypair_v2" "foo" {
region = "%s"
name = "test-keypair-tf"
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAjpC1hwiOCCmKEWxJ4qzTTsJbKzndLo1BCz5PcwtUnflmU+gHJtWMZKpuEGVi29h0A/+ydKek1O18k10Ff+4tyFjiHDQAT9+OfgWf7+b1yK+qDip3X1C0UPMbwHlTfSGWLGZquwhvEFx9k3h/M+VtMvwR1lJ9LUyTAImnNjWG7TAIPmui30HvM2UiFEmqkr4ijq45MyX2+fLIePLRIFuu1p4whjHAQYufqyno3BS48icQb4p6iVEZPo4AE2o9oIyQvj2mx4dk5Y8CgSETOZTYDOR3rU2fZTRDRgPJDH9FWvQjF5tA0p3d9CoWWd2s6GKKbfoUIi8R/Db1BSPJwkqB jrp-hp-pc"
}`,
OS_REGION_NAME)

View File

@ -0,0 +1,294 @@
package openstack
import (
"bytes"
"fmt"
"log"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
)
func resourceComputeSecGroupV2() *schema.Resource {
return &schema.Resource{
Create: resourceComputeSecGroupV2Create,
Read: resourceComputeSecGroupV2Read,
Update: resourceComputeSecGroupV2Update,
Delete: resourceComputeSecGroupV2Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"description": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"rule": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"from_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: false,
},
"to_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: false,
},
"ip_protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"cidr": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"from_group_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"self": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: false,
},
},
},
},
},
}
}
func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
createOpts := secgroups.CreateOpts{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
sg, err := secgroups.Create(computeClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack security group: %s", err)
}
d.SetId(sg.ID)
createRuleOptsList := resourceSecGroupRulesV2(d)
for _, createRuleOpts := range createRuleOptsList {
_, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack security group rule: %s", err)
}
}
return resourceComputeSecGroupV2Read(d, meta)
}
func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
sg, err := secgroups.Get(computeClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "security group")
}
d.Set("name", sg.Name)
d.Set("description", sg.Description)
rtm := rulesToMap(sg.Rules)
for _, v := range rtm {
if v["group"] == d.Get("name") {
v["self"] = "1"
} else {
v["self"] = "0"
}
}
log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rtm)
d.Set("rule", rtm)
return nil
}
func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
updateOpts := secgroups.UpdateOpts{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
}
log.Printf("[DEBUG] Updating Security Group (%s) with options: %+v", d.Id(), updateOpts)
_, err = secgroups.Update(computeClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err)
}
if d.HasChange("rule") {
oldSGRaw, newSGRaw := d.GetChange("rule")
oldSGRSlice, newSGRSlice := oldSGRaw.([]interface{}), newSGRaw.([]interface{})
oldSGRSet := schema.NewSet(secgroupRuleV2Hash, oldSGRSlice)
newSGRSet := schema.NewSet(secgroupRuleV2Hash, newSGRSlice)
secgrouprulesToAdd := newSGRSet.Difference(oldSGRSet)
secgrouprulesToRemove := oldSGRSet.Difference(newSGRSet)
log.Printf("[DEBUG] Security group rules to add: %v", secgrouprulesToAdd)
log.Printf("[DEBUG] Security groups rules to remove: %v", secgrouprulesToRemove)
for _, rawRule := range secgrouprulesToAdd.List() {
createRuleOpts := resourceSecGroupRuleCreateOptsV2(d, rawRule)
rule, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract()
if err != nil {
return fmt.Errorf("Error adding rule to OpenStack security group (%s): %s", d.Id(), err)
}
log.Printf("[DEBUG] Added rule (%s) to OpenStack security group (%s) ", rule.ID, d.Id())
}
for _, r := range secgrouprulesToRemove.List() {
rule := resourceSecGroupRuleV2(d, r)
err := secgroups.DeleteRule(computeClient, rule.ID).ExtractErr()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
}
if errCode.Actual == 404 {
continue
} else {
return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s)", rule.ID, d.Id())
}
} else {
log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
}
}
}
return resourceComputeSecGroupV2Read(d, meta)
}
func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
computeClient, err := config.computeV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
}
err = secgroups.Delete(computeClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack security group: %s", err)
}
d.SetId("")
return nil
}
func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts {
rawRules := (d.Get("rule")).([]interface{})
createRuleOptsList := make([]secgroups.CreateRuleOpts, len(rawRules))
for i, raw := range rawRules {
rawMap := raw.(map[string]interface{})
groupId := rawMap["from_group_id"].(string)
if rawMap["self"].(bool) {
groupId = d.Id()
}
createRuleOptsList[i] = secgroups.CreateRuleOpts{
ParentGroupID: d.Id(),
FromPort: rawMap["from_port"].(int),
ToPort: rawMap["to_port"].(int),
IPProtocol: rawMap["ip_protocol"].(string),
CIDR: rawMap["cidr"].(string),
FromGroupID: groupId,
}
}
return createRuleOptsList
}
func resourceSecGroupRuleCreateOptsV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts {
rawMap := raw.(map[string]interface{})
groupId := rawMap["from_group_id"].(string)
if rawMap["self"].(bool) {
groupId = d.Id()
}
return secgroups.CreateRuleOpts{
ParentGroupID: d.Id(),
FromPort: rawMap["from_port"].(int),
ToPort: rawMap["to_port"].(int),
IPProtocol: rawMap["ip_protocol"].(string),
CIDR: rawMap["cidr"].(string),
FromGroupID: groupId,
}
}
func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.Rule {
rawMap := raw.(map[string]interface{})
return secgroups.Rule{
ID: rawMap["id"].(string),
ParentGroupID: d.Id(),
FromPort: rawMap["from_port"].(int),
ToPort: rawMap["to_port"].(int),
IPProtocol: rawMap["ip_protocol"].(string),
IPRange: secgroups.IPRange{CIDR: rawMap["cidr"].(string)},
}
}
func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} {
sgrMap := make([]map[string]interface{}, len(sgrs))
for i, sgr := range sgrs {
sgrMap[i] = map[string]interface{}{
"id": sgr.ID,
"from_port": sgr.FromPort,
"to_port": sgr.ToPort,
"ip_protocol": sgr.IPProtocol,
"cidr": sgr.IPRange.CIDR,
"group": sgr.Group.Name,
}
}
return sgrMap
}
func secgroupRuleV2Hash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int)))
buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int)))
buf.WriteString(fmt.Sprintf("%s-", m["ip_protocol"].(string)))
buf.WriteString(fmt.Sprintf("%s-", m["cidr"].(string)))
return hashcode.String(buf.String())
}

View File

@ -0,0 +1,90 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
)
func TestAccComputeV2SecGroup_basic(t *testing.T) {
var secgroup secgroups.SecurityGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeV2SecGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeV2SecGroup_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.foo", &secgroup),
),
},
},
})
}
func testAccCheckComputeV2SecGroupDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckComputeV2SecGroupDestroy) Error creating OpenStack compute client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_compute_secgroup_v2" {
continue
}
_, err := secgroups.Get(computeClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Security group still exists")
}
}
return nil
}
func testAccCheckComputeV2SecGroupExists(t *testing.T, n string, secgroup *secgroups.SecurityGroup) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckComputeV2SecGroupExists) Error creating OpenStack compute client: %s", err)
}
found, err := secgroups.Get(computeClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Security group not found")
}
*secgroup = *found
return nil
}
}
var testAccComputeV2SecGroup_basic = fmt.Sprintf(`
resource "openstack_compute_secgroup_v2" "foo" {
region = "%s"
name = "test_group_1"
description = "first test security group"
}`,
OS_REGION_NAME)

View File

@ -0,0 +1,242 @@
package openstack
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
)
func resourceFWFirewallV1() *schema.Resource {
return &schema.Resource{
Create: resourceFWFirewallV1Create,
Read: resourceFWFirewallV1Read,
Update: resourceFWFirewallV1Update,
Delete: resourceFWFirewallV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"policy_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"admin_state_up": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceFWFirewallV1Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
adminStateUp := d.Get("admin_state_up").(bool)
firewallConfiguration := firewalls.CreateOpts{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
PolicyID: d.Get("policy_id").(string),
AdminStateUp: &adminStateUp,
TenantID: d.Get("tenant_id").(string),
}
log.Printf("[DEBUG] Create firewall: %#v", firewallConfiguration)
firewall, err := firewalls.Create(networkingClient, firewallConfiguration).Extract()
if err != nil {
return err
}
log.Printf("[DEBUG] Firewall created: %#v", firewall)
stateConf := &resource.StateChangeConf{
Pending: []string{"PENDING_CREATE"},
Target: "ACTIVE",
Refresh: waitForFirewallActive(networkingClient, firewall.ID),
Timeout: 30 * time.Second,
Delay: 0,
MinTimeout: 2 * time.Second,
}
_, err = stateConf.WaitForState()
d.SetId(firewall.ID)
return resourceFWFirewallV1Read(d, meta)
}
func resourceFWFirewallV1Read(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Retrieve information about firewall: %s", d.Id())
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
firewall, err := firewalls.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "LB pool")
}
d.Set("name", firewall.Name)
d.Set("description", firewall.Description)
d.Set("policy_id", firewall.PolicyID)
d.Set("admin_state_up", firewall.AdminStateUp)
d.Set("tenant_id", firewall.TenantID)
return nil
}
func resourceFWFirewallV1Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
opts := firewalls.UpdateOpts{}
if d.HasChange("name") {
opts.Name = d.Get("name").(string)
}
if d.HasChange("description") {
opts.Description = d.Get("description").(string)
}
if d.HasChange("policy_id") {
opts.PolicyID = d.Get("policy_id").(string)
}
if d.HasChange("admin_state_up") {
adminStateUp := d.Get("admin_state_up").(bool)
opts.AdminStateUp = &adminStateUp
}
log.Printf("[DEBUG] Updating firewall with id %s: %#v", d.Id(), opts)
stateConf := &resource.StateChangeConf{
Pending: []string{"PENDING_CREATE", "PENDING_UPDATE"},
Target: "ACTIVE",
Refresh: waitForFirewallActive(networkingClient, d.Id()),
Timeout: 30 * time.Second,
Delay: 0,
MinTimeout: 2 * time.Second,
}
_, err = stateConf.WaitForState()
err = firewalls.Update(networkingClient, d.Id(), opts).Err
if err != nil {
return err
}
return resourceFWFirewallV1Read(d, meta)
}
func resourceFWFirewallV1Delete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Destroy firewall: %s", d.Id())
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
stateConf := &resource.StateChangeConf{
Pending: []string{"PENDING_CREATE", "PENDING_UPDATE"},
Target: "ACTIVE",
Refresh: waitForFirewallActive(networkingClient, d.Id()),
Timeout: 30 * time.Second,
Delay: 0,
MinTimeout: 2 * time.Second,
}
_, err = stateConf.WaitForState()
err = firewalls.Delete(networkingClient, d.Id()).Err
if err != nil {
return err
}
stateConf = &resource.StateChangeConf{
Pending: []string{"DELETING"},
Target: "DELETED",
Refresh: waitForFirewallDeletion(networkingClient, d.Id()),
Timeout: 2 * time.Minute,
Delay: 0,
MinTimeout: 2 * time.Second,
}
_, err = stateConf.WaitForState()
return err
}
func waitForFirewallActive(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
fw, err := firewalls.Get(networkingClient, id).Extract()
log.Printf("[DEBUG] Get firewall %s => %#v", id, fw)
if err != nil {
return nil, "", err
}
return fw, fw.Status, nil
}
}
func waitForFirewallDeletion(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
fw, err := firewalls.Get(networkingClient, id).Extract()
log.Printf("[DEBUG] Get firewall %s => %#v", id, fw)
if err != nil {
httpStatus := err.(*gophercloud.UnexpectedResponseCodeError)
log.Printf("[DEBUG] Get firewall %s status is %d", id, httpStatus.Actual)
if httpStatus.Actual == 404 {
log.Printf("[DEBUG] Firewall %s is actually deleted", id)
return "", "DELETED", nil
}
return nil, "", fmt.Errorf("Unexpected status code %d", httpStatus.Actual)
}
log.Printf("[DEBUG] Firewall %s deletion is pending", id)
return fw, "DELETING", nil
}
}

View File

@ -0,0 +1,139 @@
package openstack
import (
"fmt"
"testing"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
)
func TestAccFWFirewallV1(t *testing.T) {
var policyID *string
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckFWFirewallV1Destroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testFirewallConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckFWFirewallV1Exists("openstack_fw_firewall_v1.accept_test", "", "", policyID),
),
},
resource.TestStep{
Config: testFirewallConfigUpdated,
Check: resource.ComposeTestCheckFunc(
testAccCheckFWFirewallV1Exists("openstack_fw_firewall_v1.accept_test", "accept_test", "terraform acceptance test", policyID),
),
},
},
})
}
func testAccCheckFWFirewallV1Destroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckOpenstackFirewallDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_firewall" {
continue
}
_, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Firewall (%s) still exists.", rs.Primary.ID)
}
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok || httpError.Actual != 404 {
return httpError
}
}
return nil
}
func testAccCheckFWFirewallV1Exists(n, expectedName, expectedDescription string, policyID *string) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckFirewallExists) Error creating OpenStack networking client: %s", err)
}
var found *firewalls.Firewall
for i := 0; i < 5; i++ {
// Firewall creation is asynchronous. Retry some times
// if we get a 404 error. Fail on any other error.
found, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok || httpError.Actual != 404 {
time.Sleep(time.Second)
continue
}
}
break
}
if err != nil {
return err
}
if found.Name != expectedName {
return fmt.Errorf("Expected Name to be <%s> but found <%s>", expectedName, found.Name)
}
if found.Description != expectedDescription {
return fmt.Errorf("Expected Description to be <%s> but found <%s>", expectedDescription, found.Description)
}
if found.PolicyID == "" {
return fmt.Errorf("Policy should not be empty")
}
if policyID != nil && found.PolicyID == *policyID {
return fmt.Errorf("Policy had not been correctly updated. Went from <%s> to <%s>", expectedName, found.Name)
}
policyID = &found.PolicyID
return nil
}
}
const testFirewallConfig = `
resource "openstack_fw_firewall_v1" "accept_test" {
policy_id = "${openstack_fw_policy_v1.accept_test_policy_1.id}"
}
resource "openstack_fw_policy_v1" "accept_test_policy_1" {
name = "policy-1"
}
`
const testFirewallConfigUpdated = `
resource "openstack_fw_firewall_v1" "accept_test" {
name = "accept_test"
description = "terraform acceptance test"
policy_id = "${openstack_fw_policy_v1.accept_test_policy_2.id}"
}
resource "openstack_fw_policy_v1" "accept_test_policy_2" {
name = "policy-2"
}
`

View File

@ -0,0 +1,200 @@
package openstack
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
)
func resourceFWPolicyV1() *schema.Resource {
return &schema.Resource{
Create: resourceFWPolicyV1Create,
Read: resourceFWPolicyV1Read,
Update: resourceFWPolicyV1Update,
Delete: resourceFWPolicyV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"audited": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"shared": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"rules": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int {
return hashcode.String(v.(string))
},
},
},
}
}
func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
v := d.Get("rules").(*schema.Set)
log.Printf("[DEBUG] Rules found : %#v", v)
log.Printf("[DEBUG] Rules count : %d", v.Len())
rules := make([]string, v.Len())
for i, v := range v.List() {
rules[i] = v.(string)
}
audited := d.Get("audited").(bool)
shared := d.Get("shared").(bool)
opts := policies.CreateOpts{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Audited: &audited,
Shared: &shared,
TenantID: d.Get("tenant_id").(string),
Rules: rules,
}
log.Printf("[DEBUG] Create firewall policy: %#v", opts)
policy, err := policies.Create(networkingClient, opts).Extract()
if err != nil {
return err
}
log.Printf("[DEBUG] Firewall policy created: %#v", policy)
d.SetId(policy.ID)
return resourceFWPolicyV1Read(d, meta)
}
func resourceFWPolicyV1Read(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Retrieve information about firewall policy: %s", d.Id())
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
policy, err := policies.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "LB pool")
}
d.Set("name", policy.Name)
d.Set("description", policy.Description)
d.Set("shared", policy.Shared)
d.Set("audited", policy.Audited)
d.Set("tenant_id", policy.TenantID)
return nil
}
func resourceFWPolicyV1Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
opts := policies.UpdateOpts{}
if d.HasChange("name") {
opts.Name = d.Get("name").(string)
}
if d.HasChange("description") {
opts.Description = d.Get("description").(string)
}
if d.HasChange("rules") {
v := d.Get("rules").(*schema.Set)
log.Printf("[DEBUG] Rules found : %#v", v)
log.Printf("[DEBUG] Rules count : %d", v.Len())
rules := make([]string, v.Len())
for i, v := range v.List() {
rules[i] = v.(string)
}
opts.Rules = rules
}
log.Printf("[DEBUG] Updating firewall policy with id %s: %#v", d.Id(), opts)
err = policies.Update(networkingClient, d.Id(), opts).Err
if err != nil {
return err
}
return resourceFWPolicyV1Read(d, meta)
}
func resourceFWPolicyV1Delete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Destroy firewall policy: %s", d.Id())
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
for i := 0; i < 15; i++ {
err = policies.Delete(networkingClient, d.Id()).Err
if err == nil {
break
}
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok || httpError.Actual != 409 {
return err
}
// This error usualy means that the policy is attached
// to a firewall. At this point, the firewall is probably
// being delete. So, we retry a few times.
time.Sleep(time.Second * 2)
}
return err
}

View File

@ -0,0 +1,165 @@
package openstack
import (
"fmt"
"testing"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
)
func TestAccFWPolicyV1(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckFWPolicyV1Destroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testFirewallPolicyConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckFWPolicyV1Exists(
"openstack_fw_policy_v1.accept_test",
"", "", 0),
),
},
resource.TestStep{
Config: testFirewallPolicyConfigAddRules,
Check: resource.ComposeTestCheckFunc(
testAccCheckFWPolicyV1Exists(
"openstack_fw_policy_v1.accept_test",
"accept_test", "terraform acceptance test", 2),
),
},
resource.TestStep{
Config: testFirewallPolicyUpdateDeleteRule,
Check: resource.ComposeTestCheckFunc(
testAccCheckFWPolicyV1Exists(
"openstack_fw_policy_v1.accept_test",
"accept_test", "terraform acceptance test", 1),
),
},
},
})
}
func testAccCheckFWPolicyV1Destroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckOpenstackFirewallPolicyDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_fw_policy_v1" {
continue
}
_, err = policies.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Firewall policy (%s) still exists.", rs.Primary.ID)
}
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok || httpError.Actual != 404 {
return httpError
}
}
return nil
}
func testAccCheckFWPolicyV1Exists(n, name, description string, ruleCount int) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckFirewallPolicyExists) Error creating OpenStack networking client: %s", err)
}
var found *policies.Policy
for i := 0; i < 5; i++ {
// Firewall policy creation is asynchronous. Retry some times
// if we get a 404 error. Fail on any other error.
found, err = policies.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok || httpError.Actual != 404 {
time.Sleep(time.Second)
continue
}
}
break
}
if err != nil {
return err
}
if name != found.Name {
return fmt.Errorf("Expected name <%s>, but found <%s>", name, found.Name)
}
if description != found.Description {
return fmt.Errorf("Expected description <%s>, but found <%s>", description, found.Description)
}
if ruleCount != len(found.Rules) {
return fmt.Errorf("Expected rule count <%d>, but found <%d>", ruleCount, len(found.Rules))
}
return nil
}
}
const testFirewallPolicyConfig = `
resource "openstack_fw_policy_v1" "accept_test" {
}
`
const testFirewallPolicyConfigAddRules = `
resource "openstack_fw_policy_v1" "accept_test" {
name = "accept_test"
description = "terraform acceptance test"
rules = [
"${openstack_fw_rule_v1.accept_test_udp_deny.id}",
"${openstack_fw_rule_v1.accept_test_tcp_allow.id}"
]
}
resource "openstack_fw_rule_v1" "accept_test_tcp_allow" {
protocol = "tcp"
action = "allow"
}
resource "openstack_fw_rule_v1" "accept_test_udp_deny" {
protocol = "udp"
action = "deny"
}
`
const testFirewallPolicyUpdateDeleteRule = `
resource "openstack_fw_policy_v1" "accept_test" {
name = "accept_test"
description = "terraform acceptance test"
rules = [
"${openstack_fw_rule_v1.accept_test_udp_deny.id}"
]
}
resource "openstack_fw_rule_v1" "accept_test_udp_deny" {
protocol = "udp"
action = "deny"
}
`

View File

@ -0,0 +1,223 @@
package openstack
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
)
func resourceFWRuleV1() *schema.Resource {
return &schema.Resource{
Create: resourceFWRuleV1Create,
Read: resourceFWRuleV1Read,
Update: resourceFWRuleV1Update,
Delete: resourceFWRuleV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"action": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"ip_version": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 4,
},
"source_ip_address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"destination_ip_address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"source_port": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"destination_port": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"enabled": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceFWRuleV1Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
enabled := d.Get("enabled").(bool)
ruleConfiguration := rules.CreateOpts{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Protocol: d.Get("protocol").(string),
Action: d.Get("action").(string),
IPVersion: d.Get("ip_version").(int),
SourceIPAddress: d.Get("source_ip_address").(string),
DestinationIPAddress: d.Get("destination_ip_address").(string),
SourcePort: d.Get("source_port").(string),
DestinationPort: d.Get("destination_port").(string),
Enabled: &enabled,
TenantID: d.Get("tenant_id").(string),
}
log.Printf("[DEBUG] Create firewall rule: %#v", ruleConfiguration)
rule, err := rules.Create(networkingClient, ruleConfiguration).Extract()
if err != nil {
return err
}
log.Printf("[DEBUG] Firewall rule with id %s : %#v", rule.ID, rule)
d.SetId(rule.ID)
return resourceFWRuleV1Read(d, meta)
}
func resourceFWRuleV1Read(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Retrieve information about firewall rule: %s", d.Id())
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
rule, err := rules.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "LB pool")
}
d.Set("protocol", rule.Protocol)
d.Set("action", rule.Action)
d.Set("name", rule.Name)
d.Set("description", rule.Description)
d.Set("ip_version", rule.IPVersion)
d.Set("source_ip_address", rule.SourceIPAddress)
d.Set("destination_ip_address", rule.DestinationIPAddress)
d.Set("source_port", rule.SourcePort)
d.Set("destination_port", rule.DestinationPort)
d.Set("enabled", rule.Enabled)
return nil
}
func resourceFWRuleV1Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
opts := rules.UpdateOpts{}
if d.HasChange("name") {
opts.Name = d.Get("name").(string)
}
if d.HasChange("description") {
opts.Description = d.Get("description").(string)
}
if d.HasChange("protocol") {
opts.Protocol = d.Get("protocol").(string)
}
if d.HasChange("action") {
opts.Action = d.Get("action").(string)
}
if d.HasChange("ip_version") {
opts.IPVersion = d.Get("ip_version").(int)
}
if d.HasChange("source_ip_address") {
sourceIPAddress := d.Get("source_ip_address").(string)
opts.SourceIPAddress = &sourceIPAddress
}
if d.HasChange("destination_ip_address") {
destinationIPAddress := d.Get("destination_ip_address").(string)
opts.DestinationIPAddress = &destinationIPAddress
}
if d.HasChange("source_port") {
sourcePort := d.Get("source_port").(string)
opts.SourcePort = &sourcePort
}
if d.HasChange("destination_port") {
destinationPort := d.Get("destination_port").(string)
opts.DestinationPort = &destinationPort
}
if d.HasChange("enabled") {
enabled := d.Get("enabled").(bool)
opts.Enabled = &enabled
}
log.Printf("[DEBUG] Updating firewall rules: %#v", opts)
err = rules.Update(networkingClient, d.Id(), opts).Err
if err != nil {
return err
}
return resourceFWRuleV1Read(d, meta)
}
func resourceFWRuleV1Delete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Destroy firewall rule: %s", d.Id())
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
rule, err := rules.Get(networkingClient, d.Id()).Extract()
if err != nil {
return err
}
if rule.PolicyID != "" {
err := policies.RemoveRule(networkingClient, rule.PolicyID, rule.ID)
if err != nil {
return err
}
}
return rules.Delete(networkingClient, d.Id()).Err
}

View File

@ -0,0 +1,185 @@
package openstack
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
)
func TestAccFWRuleV1(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckFWRuleV1Destroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testFirewallRuleMinimalConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckFWRuleV1Exists(
"openstack_fw_rule_v1.accept_test_minimal",
&rules.Rule{
Protocol: "udp",
Action: "deny",
IPVersion: 4,
Enabled: true,
}),
),
},
resource.TestStep{
Config: testFirewallRuleConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckFWRuleV1Exists(
"openstack_fw_rule_v1.accept_test",
&rules.Rule{
Name: "accept_test",
Protocol: "udp",
Action: "deny",
Description: "Terraform accept test",
IPVersion: 4,
SourceIPAddress: "1.2.3.4",
DestinationIPAddress: "4.3.2.0/24",
SourcePort: "444",
DestinationPort: "555",
Enabled: true,
}),
),
},
resource.TestStep{
Config: testFirewallRuleUpdateAllFieldsConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckFWRuleV1Exists(
"openstack_fw_rule_v1.accept_test",
&rules.Rule{
Name: "accept_test_updated_2",
Protocol: "tcp",
Action: "allow",
Description: "Terraform accept test updated",
IPVersion: 4,
SourceIPAddress: "1.2.3.0/24",
DestinationIPAddress: "4.3.2.8",
SourcePort: "666",
DestinationPort: "777",
Enabled: false,
}),
),
},
},
})
}
func testAccCheckFWRuleV1Destroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckOpenstackFirewallRuleDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_firewall_rule" {
continue
}
_, err = rules.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Firewall rule (%s) still exists.", rs.Primary.ID)
}
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok || httpError.Actual != 404 {
return httpError
}
}
return nil
}
func testAccCheckFWRuleV1Exists(n string, expected *rules.Rule) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckFirewallRuleExists) Error creating OpenStack networking client: %s", err)
}
var found *rules.Rule
for i := 0; i < 5; i++ {
// Firewall rule creation is asynchronous. Retry some times
// if we get a 404 error. Fail on any other error.
found, err = rules.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok || httpError.Actual != 404 {
time.Sleep(time.Second)
continue
}
}
break
}
if err != nil {
return err
}
expected.ID = found.ID
// Erase the tenant id because we don't want to compare
// it as long it is not present in the expected
found.TenantID = ""
if !reflect.DeepEqual(expected, found) {
return fmt.Errorf("Expected:\n%#v\nFound:\n%#v", expected, found)
}
return nil
}
}
const testFirewallRuleMinimalConfig = `
resource "openstack_fw_rule_v1" "accept_test_minimal" {
protocol = "udp"
action = "deny"
}
`
const testFirewallRuleConfig = `
resource "openstack_fw_rule_v1" "accept_test" {
name = "accept_test"
description = "Terraform accept test"
protocol = "udp"
action = "deny"
ip_version = 4
source_ip_address = "1.2.3.4"
destination_ip_address = "4.3.2.0/24"
source_port = "444"
destination_port = "555"
enabled = true
}
`
const testFirewallRuleUpdateAllFieldsConfig = `
resource "openstack_fw_rule_v1" "accept_test" {
name = "accept_test_updated_2"
description = "Terraform accept test updated"
protocol = "tcp"
action = "allow"
ip_version = 4
source_ip_address = "1.2.3.0/24"
destination_ip_address = "4.3.2.8"
source_port = "666"
destination_port = "777"
enabled = false
}
`

View File

@ -0,0 +1,192 @@
package openstack
import (
"fmt"
"log"
"strconv"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
)
func resourceLBMonitorV1() *schema.Resource {
return &schema.Resource{
Create: resourceLBMonitorV1Create,
Read: resourceLBMonitorV1Read,
Update: resourceLBMonitorV1Update,
Delete: resourceLBMonitorV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"delay": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: false,
},
"timeout": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: false,
},
"max_retries": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: false,
},
"url_path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"http_method": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"expected_codes": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"admin_state_up": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
},
}
}
func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
createOpts := monitors.CreateOpts{
TenantID: d.Get("tenant_id").(string),
Type: d.Get("type").(string),
Delay: d.Get("delay").(int),
Timeout: d.Get("timeout").(int),
MaxRetries: d.Get("max_retries").(int),
URLPath: d.Get("url_path").(string),
ExpectedCodes: d.Get("expected_codes").(string),
HTTPMethod: d.Get("http_method").(string),
}
asuRaw := d.Get("admin_state_up").(string)
if asuRaw != "" {
asu, err := strconv.ParseBool(asuRaw)
if err != nil {
return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
}
createOpts.AdminStateUp = &asu
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
m, err := monitors.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack LB Monitor: %s", err)
}
log.Printf("[INFO] LB Monitor ID: %s", m.ID)
d.SetId(m.ID)
return resourceLBMonitorV1Read(d, meta)
}
func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
m, err := monitors.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "LB monitor")
}
log.Printf("[DEBUG] Retreived OpenStack LB Monitor %s: %+v", d.Id(), m)
d.Set("type", m.Type)
d.Set("delay", m.Delay)
d.Set("timeout", m.Timeout)
d.Set("max_retries", m.MaxRetries)
d.Set("tenant_id", m.TenantID)
d.Set("url_path", m.URLPath)
d.Set("http_method", m.HTTPMethod)
d.Set("expected_codes", m.ExpectedCodes)
d.Set("admin_state_up", strconv.FormatBool(m.AdminStateUp))
return nil
}
func resourceLBMonitorV1Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
updateOpts := monitors.UpdateOpts{
Delay: d.Get("delay").(int),
Timeout: d.Get("timeout").(int),
MaxRetries: d.Get("max_retries").(int),
URLPath: d.Get("url_path").(string),
HTTPMethod: d.Get("http_method").(string),
ExpectedCodes: d.Get("expected_codes").(string),
}
if d.HasChange("admin_state_up") {
asuRaw := d.Get("admin_state_up").(string)
if asuRaw != "" {
asu, err := strconv.ParseBool(asuRaw)
if err != nil {
return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
}
updateOpts.AdminStateUp = &asu
}
}
log.Printf("[DEBUG] Updating OpenStack LB Monitor %s with options: %+v", d.Id(), updateOpts)
_, err = monitors.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack LB Monitor: %s", err)
}
return resourceLBMonitorV1Read(d, meta)
}
func resourceLBMonitorV1Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
err = monitors.Delete(networkingClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack LB Monitor: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,110 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
)
func TestAccLBV1Monitor_basic(t *testing.T) {
var monitor monitors.Monitor
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLBV1MonitorDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccLBV1Monitor_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckLBV1MonitorExists(t, "openstack_lb_monitor_v1.monitor_1", &monitor),
),
},
resource.TestStep{
Config: testAccLBV1Monitor_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_lb_monitor_v1.monitor_1", "delay", "20"),
),
},
},
})
}
func testAccCheckLBV1MonitorDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckLBV1MonitorDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_lb_monitor_v1" {
continue
}
_, err := monitors.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("LB monitor still exists")
}
}
return nil
}
func testAccCheckLBV1MonitorExists(t *testing.T, n string, monitor *monitors.Monitor) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckLBV1MonitorExists) Error creating OpenStack networking client: %s", err)
}
found, err := monitors.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Monitor not found")
}
*monitor = *found
return nil
}
}
var testAccLBV1Monitor_basic = fmt.Sprintf(`
resource "openstack_lb_monitor_v1" "monitor_1" {
region = "%s"
type = "PING"
delay = 30
timeout = 5
max_retries = 3
admin_state_up = "true"
}`,
OS_REGION_NAME)
var testAccLBV1Monitor_update = fmt.Sprintf(`
resource "openstack_lb_monitor_v1" "monitor_1" {
region = "%s"
type = "PING"
delay = 20
timeout = 5
max_retries = 3
admin_state_up = "true"
}`,
OS_REGION_NAME)

View File

@ -0,0 +1,327 @@
package openstack
import (
"bytes"
"fmt"
"log"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
"github.com/rackspace/gophercloud/pagination"
)
func resourceLBPoolV1() *schema.Resource {
return &schema.Resource{
Create: resourceLBPoolV1Create,
Read: resourceLBPoolV1Read,
Update: resourceLBPoolV1Update,
Delete: resourceLBPoolV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"subnet_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"lb_method": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"member": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"address": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"admin_state_up": &schema.Schema{
Type: schema.TypeBool,
Required: true,
ForceNew: false,
},
},
},
Set: resourceLBMemberV1Hash,
},
"monitor_ids": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int {
return hashcode.String(v.(string))
},
},
},
}
}
func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
createOpts := pools.CreateOpts{
Name: d.Get("name").(string),
Protocol: d.Get("protocol").(string),
SubnetID: d.Get("subnet_id").(string),
LBMethod: d.Get("lb_method").(string),
TenantID: d.Get("tenant_id").(string),
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
p, err := pools.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack LB pool: %s", err)
}
log.Printf("[INFO] LB Pool ID: %s", p.ID)
d.SetId(p.ID)
if mIDs := resourcePoolMonitorIDsV1(d); mIDs != nil {
for _, mID := range mIDs {
_, err := pools.AssociateMonitor(networkingClient, p.ID, mID).Extract()
if err != nil {
return fmt.Errorf("Error associating monitor (%s) with OpenStack LB pool (%s): %s", mID, p.ID, err)
}
}
}
if memberOpts := resourcePoolMembersV1(d); memberOpts != nil {
for _, memberOpt := range memberOpts {
_, err := members.Create(networkingClient, memberOpt).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack LB member: %s", err)
}
}
}
return resourceLBPoolV1Read(d, meta)
}
func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
p, err := pools.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "LB pool")
}
log.Printf("[DEBUG] Retreived OpenStack LB Pool %s: %+v", d.Id(), p)
d.Set("name", p.Name)
d.Set("protocol", p.Protocol)
d.Set("subnet_id", p.SubnetID)
d.Set("lb_method", p.LBMethod)
d.Set("tenant_id", p.TenantID)
d.Set("monitor_ids", p.MonitorIDs)
d.Set("member_ids", p.MemberIDs)
return nil
}
func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
var updateOpts pools.UpdateOpts
if d.HasChange("name") {
updateOpts.Name = d.Get("name").(string)
}
if d.HasChange("lb_method") {
updateOpts.LBMethod = d.Get("lb_method").(string)
}
log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts)
_, err = pools.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack LB Pool: %s", err)
}
if d.HasChange("monitor_ids") {
oldMIDsRaw, newMIDsRaw := d.GetChange("security_groups")
oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set)
monitorsToAdd := newMIDsSet.Difference(oldMIDsSet)
monitorsToRemove := oldMIDsSet.Difference(newMIDsSet)
log.Printf("[DEBUG] Monitors to add: %v", monitorsToAdd)
log.Printf("[DEBUG] Monitors to remove: %v", monitorsToRemove)
for _, m := range monitorsToAdd.List() {
_, err := pools.AssociateMonitor(networkingClient, d.Id(), m.(string)).Extract()
if err != nil {
return fmt.Errorf("Error associating monitor (%s) with OpenStack server (%s): %s", m.(string), d.Id(), err)
}
log.Printf("[DEBUG] Associated monitor (%s) with pool (%s)", m.(string), d.Id())
}
for _, m := range monitorsToRemove.List() {
_, err := pools.DisassociateMonitor(networkingClient, d.Id(), m.(string)).Extract()
if err != nil {
return fmt.Errorf("Error disassociating monitor (%s) from OpenStack server (%s): %s", m.(string), d.Id(), err)
}
log.Printf("[DEBUG] Disassociated monitor (%s) from pool (%s)", m.(string), d.Id())
}
}
if d.HasChange("member") {
oldMembersRaw, newMembersRaw := d.GetChange("member")
oldMembersSet, newMembersSet := oldMembersRaw.(*schema.Set), newMembersRaw.(*schema.Set)
membersToAdd := newMembersSet.Difference(oldMembersSet)
membersToRemove := oldMembersSet.Difference(newMembersSet)
log.Printf("[DEBUG] Members to add: %v", membersToAdd)
log.Printf("[DEBUG] Members to remove: %v", membersToRemove)
for _, m := range membersToRemove.List() {
oldMember := resourcePoolMemberV1(d, m)
listOpts := members.ListOpts{
PoolID: d.Id(),
Address: oldMember.Address,
ProtocolPort: oldMember.ProtocolPort,
}
err = members.List(networkingClient, listOpts).EachPage(func(page pagination.Page) (bool, error) {
extractedMembers, err := members.ExtractMembers(page)
if err != nil {
return false, err
}
for _, member := range extractedMembers {
err := members.Delete(networkingClient, member.ID).ExtractErr()
if err != nil {
return false, fmt.Errorf("Error deleting member (%s) from OpenStack LB pool (%s): %s", member.ID, d.Id(), err)
}
log.Printf("[DEBUG] Deleted member (%s) from pool (%s)", member.ID, d.Id())
}
return true, nil
})
}
for _, m := range membersToAdd.List() {
createOpts := resourcePoolMemberV1(d, m)
newMember, err := members.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating LB member: %s", err)
}
log.Printf("[DEBUG] Created member (%s) in OpenStack LB pool (%s)", newMember.ID, d.Id())
}
}
return resourceLBPoolV1Read(d, meta)
}
func resourceLBPoolV1Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
err = pools.Delete(networkingClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err)
}
d.SetId("")
return nil
}
func resourcePoolMonitorIDsV1(d *schema.ResourceData) []string {
mIDsRaw := d.Get("monitor_ids").(*schema.Set)
mIDs := make([]string, mIDsRaw.Len())
for i, raw := range mIDsRaw.List() {
mIDs[i] = raw.(string)
}
return mIDs
}
func resourcePoolMembersV1(d *schema.ResourceData) []members.CreateOpts {
memberOptsRaw := (d.Get("member")).(*schema.Set)
memberOpts := make([]members.CreateOpts, memberOptsRaw.Len())
for i, raw := range memberOptsRaw.List() {
rawMap := raw.(map[string]interface{})
memberOpts[i] = members.CreateOpts{
TenantID: rawMap["tenant_id"].(string),
Address: rawMap["address"].(string),
ProtocolPort: rawMap["port"].(int),
PoolID: d.Id(),
}
}
return memberOpts
}
func resourcePoolMemberV1(d *schema.ResourceData, raw interface{}) members.CreateOpts {
rawMap := raw.(map[string]interface{})
return members.CreateOpts{
TenantID: rawMap["tenant_id"].(string),
Address: rawMap["address"].(string),
ProtocolPort: rawMap["port"].(int),
PoolID: d.Id(),
}
}
func resourceLBMemberV1Hash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m["region"].(string)))
buf.WriteString(fmt.Sprintf("%s-", m["tenant_id"].(string)))
buf.WriteString(fmt.Sprintf("%s-", m["address"].(string)))
buf.WriteString(fmt.Sprintf("%d-", m["port"].(int)))
return hashcode.String(buf.String())
}

View File

@ -0,0 +1,134 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
)
func TestAccLBV1Pool_basic(t *testing.T) {
var pool pools.Pool
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLBV1PoolDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccLBV1Pool_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckLBV1PoolExists(t, "openstack_lb_pool_v1.pool_1", &pool),
),
},
resource.TestStep{
Config: testAccLBV1Pool_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_lb_pool_v1.pool_1", "name", "tf_test_lb_pool_updated"),
),
},
},
})
}
func testAccCheckLBV1PoolDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckLBV1PoolDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_lb_pool_v1" {
continue
}
_, err := pools.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("LB Pool still exists")
}
}
return nil
}
func testAccCheckLBV1PoolExists(t *testing.T, n string, pool *pools.Pool) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckLBV1PoolExists) Error creating OpenStack networking client: %s", err)
}
found, err := pools.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Pool not found")
}
*pool = *found
return nil
}
}
var testAccLBV1Pool_basic = fmt.Sprintf(`
resource "openstack_networking_network_v2" "network_1" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
region = "%s"
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}
resource "openstack_lb_pool_v1" "pool_1" {
region = "%s"
name = "tf_test_lb_pool"
protocol = "HTTP"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
lb_method = "ROUND_ROBIN"
}`,
OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME)
var testAccLBV1Pool_update = fmt.Sprintf(`
resource "openstack_networking_network_v2" "network_1" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
region = "%s"
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}
resource "openstack_lb_pool_v1" "pool_1" {
region = "%s"
name = "tf_test_lb_pool_updated"
protocol = "HTTP"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
lb_method = "ROUND_ROBIN"
}`,
OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME)

View File

@ -0,0 +1,258 @@
package openstack
import (
"fmt"
"log"
"strconv"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
)
func resourceLBVipV1() *schema.Resource {
return &schema.Resource{
Create: resourceLBVipV1Create,
Read: resourceLBVipV1Read,
Update: resourceLBVipV1Update,
Delete: resourceLBVipV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"subnet_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"pool_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"persistence": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
ForceNew: false,
},
"conn_limit": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
},
"admin_state_up": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
},
}
}
func resourceLBVipV1Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
createOpts := vips.CreateOpts{
Name: d.Get("name").(string),
SubnetID: d.Get("subnet_id").(string),
Protocol: d.Get("protocol").(string),
ProtocolPort: d.Get("port").(int),
PoolID: d.Get("pool_id").(string),
TenantID: d.Get("tenant_id").(string),
Address: d.Get("address").(string),
Description: d.Get("description").(string),
Persistence: resourceVipPersistenceV1(d),
ConnLimit: gophercloud.MaybeInt(d.Get("conn_limit").(int)),
}
asuRaw := d.Get("admin_state_up").(string)
if asuRaw != "" {
asu, err := strconv.ParseBool(asuRaw)
if err != nil {
return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
}
createOpts.AdminStateUp = &asu
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
p, err := vips.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack LB VIP: %s", err)
}
log.Printf("[INFO] LB VIP ID: %s", p.ID)
d.SetId(p.ID)
return resourceLBVipV1Read(d, meta)
}
func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
p, err := vips.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "LB VIP")
}
log.Printf("[DEBUG] Retreived OpenStack LB VIP %s: %+v", d.Id(), p)
d.Set("name", p.Name)
d.Set("subnet_id", p.SubnetID)
d.Set("protocol", p.Protocol)
d.Set("port", p.ProtocolPort)
d.Set("pool_id", p.PoolID)
if t, exists := d.GetOk("tenant_id"); exists && t != "" {
d.Set("tenant_id", p.TenantID)
} else {
d.Set("tenant_id", "")
}
if t, exists := d.GetOk("address"); exists && t != "" {
d.Set("address", p.Address)
} else {
d.Set("address", "")
}
if t, exists := d.GetOk("description"); exists && t != "" {
d.Set("description", p.Description)
} else {
d.Set("description", "")
}
if t, exists := d.GetOk("persistence"); exists && t != "" {
d.Set("persistence", p.Description)
}
if t, exists := d.GetOk("conn_limit"); exists && t != "" {
d.Set("conn_limit", p.ConnLimit)
} else {
d.Set("conn_limit", "")
}
if t, exists := d.GetOk("admin_state_up"); exists && t != "" {
d.Set("admin_state_up", strconv.FormatBool(p.AdminStateUp))
} else {
d.Set("admin_state_up", "")
}
return nil
}
func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
var updateOpts vips.UpdateOpts
if d.HasChange("name") {
updateOpts.Name = d.Get("name").(string)
}
if d.HasChange("pool_id") {
updateOpts.PoolID = d.Get("pool_id").(string)
}
if d.HasChange("description") {
updateOpts.Description = d.Get("description").(string)
}
if d.HasChange("persistence") {
updateOpts.Persistence = resourceVipPersistenceV1(d)
}
if d.HasChange("conn_limit") {
updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int))
}
if d.HasChange("admin_state_up") {
asuRaw := d.Get("admin_state_up").(string)
if asuRaw != "" {
asu, err := strconv.ParseBool(asuRaw)
if err != nil {
return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
}
updateOpts.AdminStateUp = &asu
}
}
log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts)
_, err = vips.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack LB VIP: %s", err)
}
return resourceLBVipV1Read(d, meta)
}
func resourceLBVipV1Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
err = vips.Delete(networkingClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack LB VIP: %s", err)
}
d.SetId("")
return nil
}
func resourceVipPersistenceV1(d *schema.ResourceData) *vips.SessionPersistence {
rawP := d.Get("persistence").(interface{})
rawMap := rawP.(map[string]interface{})
if len(rawMap) != 0 {
p := vips.SessionPersistence{}
if t, ok := rawMap["type"]; ok {
p.Type = t.(string)
}
if c, ok := rawMap["cookie_name"]; ok {
p.CookieName = c.(string)
}
return &p
}
return nil
}

View File

@ -0,0 +1,152 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
)
func TestAccLBV1VIP_basic(t *testing.T) {
var vip vips.VirtualIP
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLBV1VIPDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccLBV1VIP_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckLBV1VIPExists(t, "openstack_lb_vip_v1.vip_1", &vip),
),
},
resource.TestStep{
Config: testAccLBV1VIP_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_lb_vip_v1.vip_1", "name", "tf_test_lb_vip_updated"),
),
},
},
})
}
func testAccCheckLBV1VIPDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckLBV1VIPDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_lb_vip_v1" {
continue
}
_, err := vips.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("LB VIP still exists")
}
}
return nil
}
func testAccCheckLBV1VIPExists(t *testing.T, n string, vip *vips.VirtualIP) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckLBV1VIPExists) Error creating OpenStack networking client: %s", err)
}
found, err := vips.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("VIP not found")
}
*vip = *found
return nil
}
}
var testAccLBV1VIP_basic = fmt.Sprintf(`
resource "openstack_networking_network_v2" "network_1" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
region = "%s"
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}
resource "openstack_lb_pool_v1" "pool_1" {
region = "%s"
name = "tf_test_lb_pool"
protocol = "HTTP"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
lb_method = "ROUND_ROBIN"
}
resource "openstack_lb_vip_v1" "vip_1" {
region = "RegionOne"
name = "tf_test_lb_vip"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
protocol = "HTTP"
port = 80
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
}`,
OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME)
var testAccLBV1VIP_update = fmt.Sprintf(`
resource "openstack_networking_network_v2" "network_1" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
region = "%s"
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}
resource "openstack_lb_pool_v1" "pool_1" {
region = "%s"
name = "tf_test_lb_pool"
protocol = "HTTP"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
lb_method = "ROUND_ROBIN"
}
resource "openstack_lb_vip_v1" "vip_1" {
region = "RegionOne"
name = "tf_test_lb_vip_updated"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
protocol = "HTTP"
port = 80
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
}`,
OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME)

View File

@ -0,0 +1,162 @@
package openstack
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/pagination"
)
func resourceNetworkingFloatingIPV2() *schema.Resource {
return &schema.Resource{
Create: resourceNetworkFloatingIPV2Create,
Read: resourceNetworkFloatingIPV2Read,
Delete: resourceNetworkFloatingIPV2Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"address": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"pool": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}
func resourceNetworkFloatingIPV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack network client: %s", err)
}
poolID, err := getNetworkID(d, meta, d.Get("pool").(string))
if err != nil {
return fmt.Errorf("Error retrieving floating IP pool name: %s", err)
}
if len(poolID) == 0 {
return fmt.Errorf("No network found with name: %s", d.Get("pool").(string))
}
createOpts := floatingips.CreateOpts{
FloatingNetworkID: poolID,
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
floatingIP, err := floatingips.Create(networkClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error allocating floating IP: %s", err)
}
d.SetId(floatingIP.ID)
return resourceNetworkFloatingIPV2Read(d, meta)
}
func resourceNetworkFloatingIPV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack network client: %s", err)
}
floatingIP, err := floatingips.Get(networkClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "floating IP")
}
d.Set("address", floatingIP.FloatingIP)
poolName, err := getNetworkName(d, meta, floatingIP.FloatingNetworkID)
if err != nil {
return fmt.Errorf("Error retrieving floating IP pool name: %s", err)
}
d.Set("pool", poolName)
return nil
}
func resourceNetworkFloatingIPV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack network client: %s", err)
}
err = floatingips.Delete(networkClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting floating IP: %s", err)
}
d.SetId("")
return nil
}
func getNetworkID(d *schema.ResourceData, meta interface{}, networkName string) (string, error) {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return "", fmt.Errorf("Error creating OpenStack network client: %s", err)
}
opts := networks.ListOpts{Name: networkName}
pager := networks.List(networkClient, opts)
networkID := ""
err = pager.EachPage(func(page pagination.Page) (bool, error) {
networkList, err := networks.ExtractNetworks(page)
if err != nil {
return false, err
}
for _, n := range networkList {
if n.Name == networkName {
networkID = n.ID
return false, nil
}
}
return true, nil
})
return networkID, err
}
func getNetworkName(d *schema.ResourceData, meta interface{}, networkID string) (string, error) {
config := meta.(*Config)
networkClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return "", fmt.Errorf("Error creating OpenStack network client: %s", err)
}
opts := networks.ListOpts{ID: networkID}
pager := networks.List(networkClient, opts)
networkName := ""
err = pager.EachPage(func(page pagination.Page) (bool, error) {
networkList, err := networks.ExtractNetworks(page)
if err != nil {
return false, err
}
for _, n := range networkList {
if n.ID == networkID {
networkName = n.Name
return false, nil
}
}
return true, nil
})
return networkName, err
}

View File

@ -0,0 +1,89 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
)
func TestAccNetworkingV2FloatingIP_basic(t *testing.T) {
var floatingIP floatingips.FloatingIP
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2FloatingIPDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2FloatingIP_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2FloatingIPExists(t, "openstack_networking_floatingip_v2.foo", &floatingIP),
),
},
},
})
}
func testAccCheckNetworkingV2FloatingIPDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2FloatingIPDestroy) Error creating OpenStack floating IP: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_networking_floatingip_v2" {
continue
}
_, err := floatingips.Get(networkClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("FloatingIP still exists")
}
}
return nil
}
func testAccCheckNetworkingV2FloatingIPExists(t *testing.T, n string, kp *floatingips.FloatingIP) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2FloatingIPExists) Error creating OpenStack networking client: %s", err)
}
found, err := floatingips.Get(networkClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("FloatingIP not found")
}
*kp = *found
return nil
}
}
var testAccNetworkingV2FloatingIP_basic = fmt.Sprintf(`
resource "openstack_networking_floatingip_v2" "foo" {
region = "%s"
pool = "%s"
}`,
OS_REGION_NAME, OS_POOL_NAME)

View File

@ -0,0 +1,170 @@
package openstack
import (
"fmt"
"log"
"strconv"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
)
func resourceNetworkingNetworkV2() *schema.Resource {
return &schema.Resource{
Create: resourceNetworkingNetworkV2Create,
Read: resourceNetworkingNetworkV2Read,
Update: resourceNetworkingNetworkV2Update,
Delete: resourceNetworkingNetworkV2Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"admin_state_up": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"shared": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
createOpts := networks.CreateOpts{
Name: d.Get("name").(string),
TenantID: d.Get("tenant_id").(string),
}
asuRaw := d.Get("admin_state_up").(string)
if asuRaw != "" {
asu, err := strconv.ParseBool(asuRaw)
if err != nil {
return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
}
createOpts.AdminStateUp = &asu
}
sharedRaw := d.Get("shared").(string)
if sharedRaw != "" {
shared, err := strconv.ParseBool(sharedRaw)
if err != nil {
return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err)
}
createOpts.Shared = &shared
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
n, err := networks.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack Neutron network: %s", err)
}
log.Printf("[INFO] Network ID: %s", n.ID)
d.SetId(n.ID)
return resourceNetworkingNetworkV2Read(d, meta)
}
func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
n, err := networks.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "network")
}
log.Printf("[DEBUG] Retreived Network %s: %+v", d.Id(), n)
d.Set("name", n.Name)
d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp))
d.Set("shared", strconv.FormatBool(n.Shared))
d.Set("tenant_id", n.TenantID)
return nil
}
func resourceNetworkingNetworkV2Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
var updateOpts networks.UpdateOpts
if d.HasChange("name") {
updateOpts.Name = d.Get("name").(string)
}
if d.HasChange("admin_state_up") {
asuRaw := d.Get("admin_state_up").(string)
if asuRaw != "" {
asu, err := strconv.ParseBool(asuRaw)
if err != nil {
return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
}
updateOpts.AdminStateUp = &asu
}
}
if d.HasChange("shared") {
sharedRaw := d.Get("shared").(string)
if sharedRaw != "" {
shared, err := strconv.ParseBool(sharedRaw)
if err != nil {
return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err)
}
updateOpts.Shared = &shared
}
}
log.Printf("[DEBUG] Updating Network %s with options: %+v", d.Id(), updateOpts)
_, err = networks.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err)
}
return resourceNetworkingNetworkV2Read(d, meta)
}
func resourceNetworkingNetworkV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
err = networks.Delete(networkingClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,104 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
)
func TestAccNetworkingV2Network_basic(t *testing.T) {
var network networks.Network
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2NetworkDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2Network_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2NetworkExists(t, "openstack_networking_network_v2.foo", &network),
),
},
resource.TestStep{
Config: testAccNetworkingV2Network_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_networking_network_v2.foo", "name", "network_2"),
),
},
},
})
}
func testAccCheckNetworkingV2NetworkDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2NetworkDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_networking_network_v2" {
continue
}
_, err := networks.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Network still exists")
}
}
return nil
}
func testAccCheckNetworkingV2NetworkExists(t *testing.T, n string, network *networks.Network) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2NetworkExists) Error creating OpenStack networking client: %s", err)
}
found, err := networks.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Network not found")
}
*network = *found
return nil
}
}
var testAccNetworkingV2Network_basic = fmt.Sprintf(`
resource "openstack_networking_network_v2" "foo" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}`,
OS_REGION_NAME)
var testAccNetworkingV2Network_update = fmt.Sprintf(`
resource "openstack_networking_network_v2" "foo" {
region = "%s"
name = "network_2"
admin_state_up = "true"
}`,
OS_REGION_NAME)

View File

@ -0,0 +1,107 @@
package openstack
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
)
func resourceNetworkingRouterInterfaceV2() *schema.Resource {
return &schema.Resource{
Create: resourceNetworkingRouterInterfaceV2Create,
Read: resourceNetworkingRouterInterfaceV2Read,
Delete: resourceNetworkingRouterInterfaceV2Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"router_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"subnet_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}
func resourceNetworkingRouterInterfaceV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
createOpts := routers.InterfaceOpts{
SubnetID: d.Get("subnet_id").(string),
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
n, err := routers.AddInterface(networkingClient, d.Get("router_id").(string), createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack Neutron router interface: %s", err)
}
log.Printf("[INFO] Router interface Port ID: %s", n.PortID)
d.SetId(n.PortID)
return resourceNetworkingRouterInterfaceV2Read(d, meta)
}
func resourceNetworkingRouterInterfaceV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
n, err := ports.Get(networkingClient, d.Id()).Extract()
if err != nil {
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err)
}
if httpError.Actual == 404 {
d.SetId("")
return nil
}
return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err)
}
log.Printf("[DEBUG] Retreived Router Interface %s: %+v", d.Id(), n)
return nil
}
func resourceNetworkingRouterInterfaceV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
removeOpts := routers.InterfaceOpts{
SubnetID: d.Get("subnet_id").(string),
}
_, err = routers.RemoveInterface(networkingClient, d.Get("router_id").(string), removeOpts).Extract()
if err != nil {
return fmt.Errorf("Error deleting OpenStack Neutron Router Interface: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,100 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
)
func TestAccNetworkingV2RouterInterface_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2RouterInterfaceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2RouterInterface_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2RouterInterfaceExists(t, "openstack_networking_router_interface_v2.int_1"),
),
},
},
})
}
func testAccCheckNetworkingV2RouterInterfaceDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2RouterInterfaceDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_networking_router_interface_v2" {
continue
}
_, err := ports.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Router interface still exists")
}
}
return nil
}
func testAccCheckNetworkingV2RouterInterfaceExists(t *testing.T, n string) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2RouterInterfaceExists) Error creating OpenStack networking client: %s", err)
}
found, err := ports.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Router interface not found")
}
return nil
}
}
var testAccNetworkingV2RouterInterface_basic = fmt.Sprintf(`
resource "openstack_networking_router_v2" "router_1" {
name = "router_1"
admin_state_up = "true"
}
resource "openstack_networking_router_interface_v2" "int_1" {
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
router_id = "${openstack_networking_router_v2.router_1.id}"
}
resource "openstack_networking_network_v2" "network_1" {
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}`)

View File

@ -0,0 +1,169 @@
package openstack
import (
"fmt"
"log"
"strconv"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
)
func resourceNetworkingRouterV2() *schema.Resource {
return &schema.Resource{
Create: resourceNetworkingRouterV2Create,
Read: resourceNetworkingRouterV2Read,
Update: resourceNetworkingRouterV2Update,
Delete: resourceNetworkingRouterV2Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"admin_state_up": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"external_gateway": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceNetworkingRouterV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
createOpts := routers.CreateOpts{
Name: d.Get("name").(string),
TenantID: d.Get("tenant_id").(string),
}
asuRaw := d.Get("admin_state_up").(string)
if asuRaw != "" {
asu, err := strconv.ParseBool(asuRaw)
if err != nil {
return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
}
createOpts.AdminStateUp = &asu
}
externalGateway := d.Get("external_gateway").(string)
if externalGateway != "" {
gatewayInfo := routers.GatewayInfo{
NetworkID: externalGateway,
}
createOpts.GatewayInfo = &gatewayInfo
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
n, err := routers.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack Neutron router: %s", err)
}
log.Printf("[INFO] Router ID: %s", n.ID)
d.SetId(n.ID)
return resourceNetworkingRouterV2Read(d, meta)
}
func resourceNetworkingRouterV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
n, err := routers.Get(networkingClient, d.Id()).Extract()
if err != nil {
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
}
if httpError.Actual == 404 {
d.SetId("")
return nil
}
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
}
log.Printf("[DEBUG] Retreived Router %s: %+v", d.Id(), n)
d.Set("name", n.Name)
d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp))
d.Set("tenant_id", n.TenantID)
d.Set("external_gateway", n.GatewayInfo.NetworkID)
return nil
}
func resourceNetworkingRouterV2Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
var updateOpts routers.UpdateOpts
if d.HasChange("name") {
updateOpts.Name = d.Get("name").(string)
}
if d.HasChange("admin_state_up") {
asuRaw := d.Get("admin_state_up").(string)
if asuRaw != "" {
asu, err := strconv.ParseBool(asuRaw)
if err != nil {
return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
}
updateOpts.AdminStateUp = &asu
}
}
log.Printf("[DEBUG] Updating Router %s with options: %+v", d.Id(), updateOpts)
_, err = routers.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack Neutron Router: %s", err)
}
return resourceNetworkingRouterV2Read(d, meta)
}
func resourceNetworkingRouterV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
err = routers.Delete(networkingClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack Neutron Router: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,100 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
)
func TestAccNetworkingV2Router_basic(t *testing.T) {
var router routers.Router
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2RouterDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2Router_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2RouterExists(t, "openstack_networking_router_v2.foo", &router),
),
},
resource.TestStep{
Config: testAccNetworkingV2Router_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_networking_router_v2.foo", "name", "router_2"),
),
},
},
})
}
func testAccCheckNetworkingV2RouterDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2RouterDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_networking_router_v2" {
continue
}
_, err := routers.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Router still exists")
}
}
return nil
}
func testAccCheckNetworkingV2RouterExists(t *testing.T, n string, router *routers.Router) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2RouterExists) Error creating OpenStack networking client: %s", err)
}
found, err := routers.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Router not found")
}
*router = *found
return nil
}
}
var testAccNetworkingV2Router_basic = fmt.Sprintf(`
resource "openstack_networking_router_v2" "foo" {
name = "router"
admin_state_up = "true"
}`)
var testAccNetworkingV2Router_update = fmt.Sprintf(`
resource "openstack_networking_router_v2" "foo" {
name = "router_2"
admin_state_up = "true"
}`)

View File

@ -0,0 +1,272 @@
package openstack
import (
"fmt"
"log"
"strconv"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
)
func resourceNetworkingSubnetV2() *schema.Resource {
return &schema.Resource{
Create: resourceNetworkingSubnetV2Create,
Read: resourceNetworkingSubnetV2Read,
Update: resourceNetworkingSubnetV2Update,
Delete: resourceNetworkingSubnetV2Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"network_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"cidr": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"allocation_pools": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"start": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"end": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
},
},
},
"gateway_ip": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"ip_version": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"enable_dhcp": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"dns_nameservers": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int {
return hashcode.String(v.(string))
},
},
"host_routes": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: false,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"destination_cidr": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"next_hop": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
},
},
},
},
}
}
func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
createOpts := subnets.CreateOpts{
NetworkID: d.Get("network_id").(string),
CIDR: d.Get("cidr").(string),
Name: d.Get("name").(string),
TenantID: d.Get("tenant_id").(string),
AllocationPools: resourceSubnetAllocationPoolsV2(d),
GatewayIP: d.Get("gateway_ip").(string),
IPVersion: d.Get("ip_version").(int),
DNSNameservers: resourceSubnetDNSNameserversV2(d),
HostRoutes: resourceSubnetHostRoutesV2(d),
}
edRaw := d.Get("enable_dhcp").(string)
if edRaw != "" {
ed, err := strconv.ParseBool(edRaw)
if err != nil {
return fmt.Errorf("enable_dhcp, if provided, must be either 'true' or 'false'")
}
createOpts.EnableDHCP = &ed
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
s, err := subnets.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack Neutron subnet: %s", err)
}
log.Printf("[INFO] Subnet ID: %s", s.ID)
d.SetId(s.ID)
return resourceNetworkingSubnetV2Read(d, meta)
}
func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
s, err := subnets.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "subnet")
}
log.Printf("[DEBUG] Retreived Subnet %s: %+v", d.Id(), s)
d.Set("newtork_id", s.NetworkID)
d.Set("cidr", s.CIDR)
d.Set("ip_version", s.IPVersion)
d.Set("name", s.Name)
d.Set("tenant_id", s.TenantID)
d.Set("allocation_pools", s.AllocationPools)
d.Set("gateway_ip", s.GatewayIP)
d.Set("enable_dhcp", strconv.FormatBool(s.EnableDHCP))
d.Set("dns_nameservers", s.DNSNameservers)
d.Set("host_routes", s.HostRoutes)
return nil
}
func resourceNetworkingSubnetV2Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
var updateOpts subnets.UpdateOpts
if d.HasChange("name") {
updateOpts.Name = d.Get("name").(string)
}
if d.HasChange("gateway_ip") {
updateOpts.GatewayIP = d.Get("gateway_ip").(string)
}
if d.HasChange("dns_nameservers") {
updateOpts.DNSNameservers = resourceSubnetDNSNameserversV2(d)
}
if d.HasChange("host_routes") {
updateOpts.HostRoutes = resourceSubnetHostRoutesV2(d)
}
if d.HasChange("enable_dhcp") {
edRaw := d.Get("enable_dhcp").(string)
if edRaw != "" {
ed, err := strconv.ParseBool(edRaw)
if err != nil {
return fmt.Errorf("enable_dhcp, if provided, must be either 'true' or 'false'")
}
updateOpts.EnableDHCP = &ed
}
}
log.Printf("[DEBUG] Updating Subnet %s with options: %+v", d.Id(), updateOpts)
_, err = subnets.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack Neutron Subnet: %s", err)
}
return resourceNetworkingSubnetV2Read(d, meta)
}
func resourceNetworkingSubnetV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
err = subnets.Delete(networkingClient, d.Id()).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack Neutron Subnet: %s", err)
}
d.SetId("")
return nil
}
func resourceSubnetAllocationPoolsV2(d *schema.ResourceData) []subnets.AllocationPool {
rawAPs := d.Get("allocation_pools").([]interface{})
aps := make([]subnets.AllocationPool, len(rawAPs))
for i, raw := range rawAPs {
rawMap := raw.(map[string]interface{})
aps[i] = subnets.AllocationPool{
Start: rawMap["start"].(string),
End: rawMap["end"].(string),
}
}
return aps
}
func resourceSubnetDNSNameserversV2(d *schema.ResourceData) []string {
rawDNSN := d.Get("dns_nameservers").(*schema.Set)
dnsn := make([]string, rawDNSN.Len())
for i, raw := range rawDNSN.List() {
dnsn[i] = raw.(string)
}
return dnsn
}
func resourceSubnetHostRoutesV2(d *schema.ResourceData) []subnets.HostRoute {
rawHR := d.Get("host_routes").([]interface{})
hr := make([]subnets.HostRoute, len(rawHR))
for i, raw := range rawHR {
rawMap := raw.(map[string]interface{})
hr[i] = subnets.HostRoute{
DestinationCIDR: rawMap["destination_cidr"].(string),
NextHop: rawMap["next_hop"].(string),
}
}
return hr
}

View File

@ -0,0 +1,119 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
)
func TestAccNetworkingV2Subnet_basic(t *testing.T) {
var subnet subnets.Subnet
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2SubnetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2Subnet_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2SubnetExists(t, "openstack_networking_subnet_v2.subnet_1", &subnet),
),
},
resource.TestStep{
Config: testAccNetworkingV2Subnet_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "name", "tf-test-subnet"),
resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "gateway_ip", "192.68.0.1"),
),
},
},
})
}
func testAccCheckNetworkingV2SubnetDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2SubnetDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_networking_subnet_v2" {
continue
}
_, err := subnets.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Subnet still exists")
}
}
return nil
}
func testAccCheckNetworkingV2SubnetExists(t *testing.T, n string, subnet *subnets.Subnet) 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 ID is set")
}
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2SubnetExists) Error creating OpenStack networking client: %s", err)
}
found, err := subnets.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Subnet not found")
}
*subnet = *found
return nil
}
}
var testAccNetworkingV2Subnet_basic = fmt.Sprintf(`
resource "openstack_networking_network_v2" "network_1" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
region = "%s"
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}`, OS_REGION_NAME, OS_REGION_NAME)
var testAccNetworkingV2Subnet_update = fmt.Sprintf(`
resource "openstack_networking_network_v2" "network_1" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
region = "%s"
name = "tf-test-subnet"
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
gateway_ip = "192.68.0.1"
}`, OS_REGION_NAME, OS_REGION_NAME)

View File

@ -0,0 +1,148 @@
package openstack
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
)
func resourceObjectStorageContainerV1() *schema.Resource {
return &schema.Resource{
Create: resourceObjectStorageContainerV1Create,
Read: resourceObjectStorageContainerV1Read,
Update: resourceObjectStorageContainerV1Update,
Delete: resourceObjectStorageContainerV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFunc("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"container_read": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"container_sync_to": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"container_sync_key": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"container_write": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"content_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"metadata": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
ForceNew: false,
},
},
}
}
func resourceObjectStorageContainerV1Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
objectStorageClient, err := config.objectStorageV1Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack object storage client: %s", err)
}
cn := d.Get("name").(string)
createOpts := &containers.CreateOpts{
ContainerRead: d.Get("container_read").(string),
ContainerSyncTo: d.Get("container_sync_to").(string),
ContainerSyncKey: d.Get("container_sync_key").(string),
ContainerWrite: d.Get("container_write").(string),
ContentType: d.Get("content_type").(string),
Metadata: resourceContainerMetadataV2(d),
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
_, err = containers.Create(objectStorageClient, cn, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack container: %s", err)
}
log.Printf("[INFO] Container ID: %s", cn)
// Store the ID now
d.SetId(cn)
return resourceObjectStorageContainerV1Read(d, meta)
}
func resourceObjectStorageContainerV1Read(d *schema.ResourceData, meta interface{}) error {
return nil
}
func resourceObjectStorageContainerV1Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
objectStorageClient, err := config.objectStorageV1Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack object storage client: %s", err)
}
updateOpts := containers.UpdateOpts{
ContainerRead: d.Get("container_read").(string),
ContainerSyncTo: d.Get("container_sync_to").(string),
ContainerSyncKey: d.Get("container_sync_key").(string),
ContainerWrite: d.Get("container_write").(string),
ContentType: d.Get("content_type").(string),
}
if d.HasChange("metadata") {
updateOpts.Metadata = resourceContainerMetadataV2(d)
}
_, err = containers.Update(objectStorageClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack container: %s", err)
}
return resourceObjectStorageContainerV1Read(d, meta)
}
func resourceObjectStorageContainerV1Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
objectStorageClient, err := config.objectStorageV1Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack object storage client: %s", err)
}
_, err = containers.Delete(objectStorageClient, d.Id()).Extract()
if err != nil {
return fmt.Errorf("Error deleting OpenStack container: %s", err)
}
d.SetId("")
return nil
}
func resourceContainerMetadataV2(d *schema.ResourceData) map[string]string {
m := make(map[string]string)
for key, val := range d.Get("metadata").(map[string]interface{}) {
m[key] = val.(string)
}
return m
}

View File

@ -0,0 +1,77 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
)
func TestAccObjectStorageV1Container_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckObjectStorageV1ContainerDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccObjectStorageV1Container_basic,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_objectstorage_container_v1.container_1", "name", "tf-test-container"),
resource.TestCheckResourceAttr("openstack_objectstorage_container_v1.container_1", "content_type", "application/json"),
),
},
resource.TestStep{
Config: testAccObjectStorageV1Container_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_objectstorage_container_v1.container_1", "content_type", "text/plain"),
),
},
},
})
}
func testAccCheckObjectStorageV1ContainerDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
objectStorageClient, err := config.objectStorageV1Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("Error creating OpenStack object storage client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_objectstorage_container_v1" {
continue
}
_, err := containers.Get(objectStorageClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Container still exists")
}
}
return nil
}
var testAccObjectStorageV1Container_basic = fmt.Sprintf(`
resource "openstack_objectstorage_container_v1" "container_1" {
region = "%s"
name = "tf-test-container"
metadata {
test = "true"
}
content_type = "application/json"
}`,
OS_REGION_NAME)
var testAccObjectStorageV1Container_update = fmt.Sprintf(`
resource "openstack_objectstorage_container_v1" "container_1" {
region = "%s"
name = "tf-test-container"
metadata {
test = "true"
}
content_type = "text/plain"
}`,
OS_REGION_NAME)

View File

@ -0,0 +1,22 @@
package openstack
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
)
// CheckDeleted checks the error to see if it's a 404 (Not Found) and, if so,
// sets the resource ID to the empty string instead of throwing an error.
func CheckDeleted(d *schema.ResourceData, err error, msg string) error {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return fmt.Errorf("%s: %s", msg, err)
}
if errCode.Actual == 404 {
d.SetId("")
return nil
}
return fmt.Errorf("%s: %s", msg, err)
}

View File

@ -14,7 +14,7 @@ etc. Almost any infrastructure noun can be represented as a resource in Terrafor
Terraform is agnostic to the underlying platforms by supporting providers. A provider Terraform is agnostic to the underlying platforms by supporting providers. A provider
is responsible for understanding API interactions and exposing resources. Providers is responsible for understanding API interactions and exposing resources. Providers
generally are an IaaS (e.g. AWS, DigitalOcean, GCE), PaaS (e.g. Heroku, CloudFoundry), generally are an IaaS (e.g. AWS, DigitalOcean, GCE, OpenStack), PaaS (e.g. Heroku, CloudFoundry),
or SaaS services (e.g. Atlas, DNSimple, CloudFlare). or SaaS services (e.g. Atlas, DNSimple, CloudFlare).
Use the navigation to the left to read about the available providers. Use the navigation to the left to read about the available providers.

View File

@ -0,0 +1,54 @@
---
layout: "openstack"
page_title: "Provider: OpenStack"
sidebar_current: "docs-openstack-index"
description: |-
The OpenStack provider is used to interact with the many resources supported by OpenStack. The provider needs to be configured with the proper credentials before it can be used.
---
# OpenStack Provider
The OpenStack provider is used to interact with the
many resources supported by OpenStack. The provider needs to be configured
with the proper credentials before it can be used.
Use the navigation to the left to read about the available resources.
## Example Usage
```
# Configure the OpenStack Provider
provider "openstack" {
user_name = "admin"
tenant_name = "admin"
password = "pwd"
auth_url = "http://myauthurl:5000/v2.0"
}
# Create a web server
resource "openstack_compute_instance_v2" "test-server" {
...
}
```
## Configuration Reference
The following arguments are supported:
* `auth_url` - (Required)
* `user_name` - (Optional; Required for Identity V2)
* `user_id` - (Optional)
* `password` - (Optional; Required if not using `api_key`)
* `api_key` - (Optional; Required if not using `password`)
* `domain_id` - (Optional)
* `domain_name` - (Optional)
* `tenant_id` - (Optional)
* `tenant_name` - (Optional)

View File

@ -0,0 +1,68 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_blockstorage_volume_v1"
sidebar_current: "docs-openstack-resource-blockstorage-volume-v1"
description: |-
Manages a V1 volume resource within OpenStack.
---
# openstack\_blockstorage\_volume_v1
Manages a V1 volume resource within OpenStack.
## Example Usage
```
resource "openstack_blockstorage_volume_v1" "volume_1" {
region = "RegionOne"
name = "tf-test-volume"
description = "first test volume"
size = 3
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to create the volume. If
omitted, the `OS_REGION_NAME` environment variable is used. Changing this
creates a new volume.
* `size` - (Required) The size of the volume to create (in gigabytes). Changing
this creates a new volume.
* `name` - (Optional) A unique name for the volume. Changing this updates the
volume's name.
* `description` - (Optional) A description of the volume. Changing this updates
the volume's description.
* `image_id` - (Optional) The image ID from which to create the volume.
Changing this creates a new volume.
* `snapshot_id` - (Optional) The snapshot ID from which to create the volume.
Changing this creates a new volume.
* `source_vol_id` - (Optional) The volume ID from which to create the volume.
Changing this creates a new volume.
* `metadata` - (Optional) Metadata key/value pairs to associate with the volume.
Changing this updates the existing volume metadata.
* `volume_type` - (Optional) The type of volume to create (either SATA or SSD).
Changing this creates a new volume.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `size` - See Argument Reference above.
* `name` - See Argument Reference above.
* `description` - See Argument Reference above.
* `image_id` - See Argument Reference above.
* `source_vol_id` - See Argument Reference above.
* `snapshot_id` - See Argument Reference above.
* `metadata` - See Argument Reference above.
* `volume_type` - See Argument Reference above.

View File

@ -0,0 +1,120 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_compute_instance_v2"
sidebar_current: "docs-openstack-resource-compute-instance-v2"
description: |-
Manages a V2 VM instance resource within OpenStack.
---
# openstack\_compute\_instance_v2
Manages a V2 VM instance resource within OpenStack.
## Example Usage
```
resource "openstack_compute_instance_v2" "test-server" {
name = "tf-test"
image_id = "ad091b52-742f-469e-8f3c-fd81cadf0743"
flavor_id = "3"
metadata {
this = "that"
}
key_pair = "my_key_pair_name"
security_groups = ["test-group-1"]
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to create the server instance. If
omitted, the `OS_REGION_NAME` environment variable is used. Changing this
creates a new server.
* `name` - (Required) A unique name for the resource.
* `image_id` - (Optional; Required if `image_name` is empty) The image ID of
the desired image for the server. Changing this creates a new server.
* `image_name` - (Optional; Required if `image_id` is empty) The name of the
desired image for the server. Changing this creates a new server.
* `flavor_id` - (Optional; Required if `flavor_name` is empty) The flavor ID of
the desired flavor for the server. Changing this resizes the existing server.
* `flavor_name` - (Optional; Required if `flavor_id` is empty) The name of the
desired flavor for the server. Changing this resizes the existing server.
* `security_groups` - (Optional) An array of one or more security group names
to associate with the server. Changing this results in adding/removing
security groups from the existing server.
* `availability_zone` - (Optional) The availability zone in which to create
the server. Changing this creates a new server.
* `network` - (Optional) An array of one or more networks to attach to the
instance. The network object structure is documented below. Changing this
creates a new server.
* `metadata` - (Optional) Metadata key/value pairs to make available from
within the instance. Changing this updates the existing server metadata.
* `admin_pass` - (Optional) The administrative password to assign to the server.
Changing this changes the root password on the existing server.
* `key_pair` - (Optional) The name of a key pair to put on the server. The key
pair must already be created and associated with the tenant's account.
Changing this creates a new server.
* `block_device` - (Optional) The object for booting by volume. The block_device
object structure is documented below. Changing this creates a new server.
* `volume` - (Optional) Attach an existing volume to the instance. The volume
structure is described below.
The `network` block supports:
* `uuid` - (Required unless `port` is provided) The network UUID to attach to
the server.
* `port` - (Required unless `uuid` is provided) The port UUID of a network to
attach to the server.
* `fixed_ip` - (Optional) Specifies a fixed IP address to be used on this
network.
The `block_device` block supports:
* `uuid` - (Required) The UUID of the image, volume, or snapshot.
* `source_type` - (Required) The source type of the device. Must be one of
"image", "volume", or "snapshot".
* `volume_size` - (Optional) The size of the volume to create (in gigabytes).
* `boot_index` - (Optional) The boot index of the volume. It defaults to 0.
* `destination_type` - (Optional) The type that gets created. Possible values
are "volume" and "local".
The `volume` block supports:
* `volume_id` - (Required) The UUID of the volume to attach.
* `device` - (Optional) The device that the volume will be attached as. For
example: `/dev/vdc`. Omit this option to allow the volume to be
auto-assigned a device.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `name` - See Argument Reference above.
* `access_ip_v4` - See Argument Reference above.
* `access_ip_v6` - See Argument Reference above.
* `metadata` - See Argument Reference above.
* `security_groups` - See Argument Reference above.
* `flavor_ref` - See Argument Reference above.

View File

@ -0,0 +1,43 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_compute_keypair_v2"
sidebar_current: "docs-openstack-resource-compute-keypair-v2"
description: |-
Manages a V2 keypair resource within OpenStack.
---
# openstack\_compute\_keypair_v2
Manages a V2 keypair resource within OpenStack.
## Example Usage
```
resource "openstack_compute_keypair_v2" "test-keypair" {
name = "my-keypair"
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAjpC1hwiOCCmKEWxJ4qzTTsJbKzndLotBCz5PcwtUnflmU+gHJtWMZKpuEGVi29h0A/+ydKek1O18k10Ff+4tyFjiHDQAnOfgWf7+b1yK+qDip3X1C0UPMbwHlTfSGWLGZqd9LvEFx9k3h/M+VtMvwR1lJ9LUyTAImnNjWG7TaIPmui30HvM2UiFEmqkr4ijq45MyX2+fLIePLRIF61p4whjHAQYufqyno3BS48icQb4p6iVEZPo4AE2o9oIyQvj2mx4dk5Y8CgSETOZTYDOR3rU2fZTRDRgPJDH9FWvQjF5tA0p3d9CoWWd2s6GKKbfoUIi8R/Db1BSPJwkqB"
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to obtain the V2 Compute client.
Keypairs are associated with accounts, but a Compute client is needed to
create one. If omitted, the `OS_REGION_NAME` environment variable is used.
Changing this creates a new keypair.
* `name` - (Required) A unique name for the keypair. Changing this creates a new
keypair.
* `public_key` - (Required) A pregenerated OpenSSH-formatted public key.
Changing this creates a new keypair.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `name` - See Argument Reference above.
* `public_key` - See Argument Reference above.

View File

@ -0,0 +1,76 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_compute_secgroup_v2"
sidebar_current: "docs-openstack-resource-compute-secgroup-2"
description: |-
Manages a V2 security group resource within OpenStack.
---
# openstack\_compute\_secgroup_v2
Manages a V2 security group resource within OpenStack.
## Example Usage
```
resource "openstack_compute_secgroup_v2" "secgroup_1" {
name = "my_secgroup"
description = "my security group"
rule {
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to obtain the V2 Compute client.
A Compute client is needed to create a security group. If omitted, the
`OS_REGION_NAME` environment variable is used. Changing this creates a new
security group.
* `name` - (Required) A unique name for the security group. Changing this
updates the `name` of an existing security group.
* `description` - (Required) A description for the security group. Changing this
updates the `description` of an existing security group.
* `rule` - (Optional) A rule describing how the security group operates. The
rule object structure is documented below. Changing this updates the
security group rules.
The `rule` block supports:
* `from_port` - (Required) An integer representing the lower bound of the port
range to open. Changing this creates a new security group rule.
* `to_port` - (Required) An integer representing the upper bound of the port
range to open. Changing this creates a new security group rule.
* `ip_protocol` - (Required) The protocol type that will be allowed. Changing
this creates a new security group rule.
* `cidr` - (Optional) Required if `from_group_id` is empty. The IP range that
will be the source of network traffic to the security group. Use 0.0.0.0./0
to allow all IP addresses. Changing this creates a new security group rule.
* `from_group_id` - (Optional) Required if `cidr` is empty. The ID of a group
from which to forward traffic to the parent group. Changing
this creates a new security group rule.
* `self` - (Optional) Required if `cidr` and `from_group_id` is empty. If true,
the security group itself will be added as a source to this ingress rule.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `name` - See Argument Reference above.
* `description` - See Argument Reference above.
* `rule` - See Argument Reference above.

View File

@ -0,0 +1,82 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_lb_monitor_v1"
sidebar_current: "docs-openstack-resource-lb-monitor-v1"
description: |-
Manages a V1 load balancer monitor resource within OpenStack.
---
# openstack\_lb\_monitor_v1
Manages a V1 load balancer monitor resource within OpenStack.
## Example Usage
```
resource "openstack_lb_monitor_v1" "monitor_1" {
type = "PING"
delay = 30
timeout = 5
max_retries = 3
admin_state_up = "true"
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to obtain the V2 Networking client.
A Networking client is needed to create an LB monitor. If omitted, the
`OS_REGION_NAME` environment variable is used. Changing this creates a new
LB monitor.
* `type` - (Required) The type of probe, which is PING, TCP, HTTP, or HTTPS,
that is sent by the monitor to verify the member state. Changing this
creates a new monitor.
* `delay` - (Required) The time, in seconds, between sending probes to members.
Changing this creates a new monitor.
* `timeout` - (Required) Maximum number of seconds for a monitor to wait for a
ping reply before it times out. The value must be less than the delay value.
Changing this updates the timeout of the existing monitor.
* `max_retries` - (Required) Number of permissible ping failures before changing
the member's status to INACTIVE. Must be a number between 1 and 10. Changing
this updates the max_retries of the existing monitor.
* `url_path` - (Optional) Required for HTTP(S) types. URI path that will be
accessed if monitor type is HTTP or HTTPS. Changing this updates the
url_path of the existing monitor.
* `http_method` - (Optional) Required for HTTP(S) types. The HTTP method used
for requests by the monitor. If this attribute is not specified, it defaults
to "GET". Changing this updates the http_method of the existing monitor.
* `expected_codes` - (Optional) equired for HTTP(S) types. Expected HTTP codes
for a passing HTTP(S) monitor. You can either specify a single status like
"200", or a range like "200-202". Changing this updates the expected_codes
of the existing monitor.
* `admin_state_up` - (Optional) The administrative state of the monitor.
Acceptable values are "true" and "false". Changing this value updates the
state of the existing monitor.
* `tenant_id` - (Optional) The owner of the monitor. Required if admin wants to
create a monitor for another tenant. Changing this creates a new monitor.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `type` - See Argument Reference above.
* `delay` - See Argument Reference above.
* `timeout` - See Argument Reference above.
* `max_retries` - See Argument Reference above.
* `url_path` - See Argument Reference above.
* `http_method` - See Argument Reference above.
* `expected_codes` - See Argument Reference above.
* `admin_state_up` - See Argument Reference above.
* `tenant_id` - See Argument Reference above.

View File

@ -0,0 +1,90 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_lb_pool_v1"
sidebar_current: "docs-openstack-resource-lb-pool-v1"
description: |-
Manages a V1 load balancer pool resource within OpenStack.
---
# openstack\_lb\_pool_v1
Manages a V1 load balancer pool resource within OpenStack.
## Example Usage
```
resource "openstack_lb_pool_v1" "pool_1" {
name = "tf_test_lb_pool"
protocol = "HTTP"
subnet_id = "12345"
lb_method = "ROUND_ROBIN"
monitor_ids = ["67890"]
member {
address = "192.168.0.1"
port = 80
admin_state_up = "true"
}
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to obtain the V2 Networking client.
A Networking client is needed to create an LB pool. If omitted, the
`OS_REGION_NAME` environment variable is used. Changing this creates a new
LB pool.
* `name` - (Required) The name of the pool. Changing this updates the name of
the existing pool.
* `protocol` - (Required) The protocol used by the pool members, you can use
either 'TCP, 'HTTP', or 'HTTPS'. Changing this creates a new pool.
* `subnet_id` - (Required) The network on which the members of the pool will be
located. Only members that are on this network can be added to the pool.
Changing this creates a new pool.
* `lb_method` - (Required) The algorithm used to distribute load between the
members of the pool. The current specification supports 'ROUND_ROBIN' and
'LEAST_CONNECTIONS' as valid values for this attribute.
* `tenant_id` - (Optional) The owner of the pool. Required if admin wants to
create a pool member for another tenant. Changing this creates a new pool.
* `monitor_ids` - (Optional) A list of IDs of monitors to associate with the
pool.
* `member` - (Optional) An existing node to add to the pool. Changing this
updates the members of the pool. The member object structure is documented
below.
The `member` block supports:
* `address` - (Required) The IP address of the member. Changing this creates a
new member.
* `port` - (Required) An integer representing the port on which the member is
hosted. Changing this creates a new member.
* `admin_state_up` - (Optional) The administrative state of the member.
Acceptable values are 'true' and 'false'. Changing this value updates the
state of the existing member.
* `tenant_id` - (Optional) The owner of the member. Required if admin wants to
create a pool member for another tenant. Changing this creates a new member.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `name` - See Argument Reference above.
* `protocol` - See Argument Reference above.
* `subnet_id` - See Argument Reference above.
* `lb_method` - See Argument Reference above.
* `tenant_id` - See Argument Reference above.
* `monitor_id` - See Argument Reference above.
* `member` - See Argument Reference above.

View File

@ -0,0 +1,95 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_lb_vip_v1"
sidebar_current: "docs-openstack-resource-lb-vip-v1"
description: |-
Manages a V1 load balancer vip resource within OpenStack.
---
# openstack\_lb\_vip_v1
Manages a V1 load balancer vip resource within OpenStack.
## Example Usage
```
resource "openstack_lb_vip_v1" "vip_1" {
name = "tf_test_lb_vip"
subnet_id = "12345"
protocol = "HTTP"
port = 80
pool_id = "67890"
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to obtain the V2 Networking client.
A Networking client is needed to create a VIP. If omitted, the
`OS_REGION_NAME` environment variable is used. Changing this creates a new
VIP.
* `name` - (Required) The name of the vip. Changing this updates the name of
the existing vip.
* `subnet_id` - (Required) The network on which to allocate the vip's address. A
tenant can only create vips on networks authorized by policy (e.g. networks
that belong to them or networks that are shared). Changing this creates a
new vip.
* `protocol` - (Required) The protocol - can be either 'TCP, 'HTTP', or
HTTPS'. Changing this creates a new vip.
* `port` - (Required) The port on which to listen for client traffic. Changing
this creates a new vip.
* `pool_id` - (Required) The ID of the pool with which the vip is associated.
Changing this updates the pool_id of the existing vip.
* `tenant_id` - (Optional) The owner of the vip. Required if admin wants to
create a vip member for another tenant. Changing this creates a new vip.
* `address` - (Optional) The IP address of the vip. Changing this creates a new
vip.
* `description` - (Optional) Human-readable description for the vip. Changing
this updates the description of the existing vip.
* `persistence` - (Optional) Omit this field to prevent session persistence.
The persistence object structure is documented below. Changing this updates
the persistence of the existing vip.
* `conn_limit` - (Optional) The maximum number of connections allowed for the
vip. Default is -1, meaning no limit. Changing this updates the conn_limit
of the existing vip.
* `admin_state_up` - (Optional) The administrative state of the vip.
Acceptable values are "true" and "false". Changing this value updates the
state of the existing vip.
The `persistence` block supports:
* `type` - (Required) The type of persistence mode. Valid values are "SOURCE_IP",
"HTTP_COOKIE", or "APP_COOKIE".
* `cookie_name` - (Optional) The name of the cookie if persistence mode is set
appropriately.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `name` - See Argument Reference above.
* `subnet_id` - See Argument Reference above.
* `protocol` - See Argument Reference above.
* `port` - See Argument Reference above.
* `pool_id` - See Argument Reference above.
* `tenant_id` - See Argument Reference above.
* `address` - See Argument Reference above.
* `description` - See Argument Reference above.
* `persistence` - See Argument Reference above.
* `conn_limit` - See Argument Reference above.
* `admin_state_up` - See Argument Reference above.

View File

@ -0,0 +1,53 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_networking_network_v2"
sidebar_current: "docs-openstack-resource-networking-network-v2"
description: |-
Manages a V2 Neutron network resource within OpenStack.
---
# openstack\_networking\_network_v2
Manages a V2 Neutron network resource within OpenStack.
## Example Usage
```
resource "openstack_networking_network_v2" "network_1" {
name = "tf_test_network"
admin_state_up = "true"
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to obtain the V2 Networking client.
A Networking client is needed to create a Neutron network. If omitted, the
`OS_REGION_NAME` environment variable is used. Changing this creates a new
network.
* `name` - (Optional) The name of the network. Changing this updates the name of
the existing network.
* `shared` - (Optional) Specifies whether the network resource can be accessed
by any tenant or not. Changing this updates the sharing capabalities of the
existing network.
* `tenant_id` - (Optional) The owner of the newtork. Required if admin wants to
create a network for another tenant. Changing this creates a new network.
* `admin_state_up` - (Optional) The administrative state of the network.
Acceptable values are "true" and "false". Changing this value updates the
state of the existing network.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `name` - See Argument Reference above.
* `shared` - See Argument Reference above.
* `tenant_id` - See Argument Reference above.
* `admin_state_up` - See Argument Reference above.

View File

@ -0,0 +1,98 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_networking_subnet_v2"
sidebar_current: "docs-openstack-resource-networking-subnet-v2"
description: |-
Manages a V2 Neutron subnet resource within OpenStack.
---
# openstack\_networking\_subnet_v2
Manages a V2 Neutron subnet resource within OpenStack.
## Example Usage
```
resource "openstack_networking_network_v2" "network_1" {
name = "tf_test_network"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to obtain the V2 Networking client.
A Networking client is needed to create a Neutron subnet. If omitted, the
`OS_REGION_NAME` environment variable is used. Changing this creates a new
subnet.
* `network_id` - (Required) The UUID of the parent network. Changing this
creates a new subnet.
* `cidr` - (Required) CIDR representing IP range for this subnet, based on IP
version. Changing this creates a new subnet.
* `ip_version` - (Required) IP version, either 4 or 6. Changing this creates a
new subnet.
* `name` - (Optional) The name of the subnet. Changing this updates the name of
the existing subnet.
* `tenant_id` - (Optional) The owner of the subnet. Required if admin wants to
create a subnet for another tenant. Changing this creates a new subnet.
* `allocation_pools` - (Optional) An array of sub-ranges of CIDR available for
dynamic allocation to ports. The allocation_pool object structure is
documented below. Changing this creates a new subnet.
* `gateway_ip` - (Optional) Default gateway used by devices in this subnet.
Changing this updates the gateway IP of the existing subnet.
* `enable_dhcp` - (Optional) The administrative state of the network.
Acceptable values are "true" and "false". Changing this value enables or
disables the DHCP capabilities of the existing subnet.
* `dns_nameservers` - (Optional) An array of DNS name server names used by hosts
in this subnet. Changing this updates the DNS name servers for the existing
subnet.
* `host_routes` - (Optional) An array of routes that should be used by devices
with IPs from this subnet (not including local subnet route). The host_route
object structure is documented below. Changing this updates the host routes
for the existing subnet.
The `allocation_pools` block supports:
* `start` - (Required) The starting address.
* `end` - (Required) The ending address.
The `host_routes` block supports:
* `destination_cidr` - (Required) The destination CIDR.
* `next_hop` - (Required) The next hop in the route.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `network_id` - See Argument Reference above.
* `cidr` - See Argument Reference above.
* `ip_version` - See Argument Reference above.
* `name` - See Argument Reference above.
* `tenant_id` - See Argument Reference above.
* `allocation_pools` - See Argument Reference above.
* `gateway_ip` - See Argument Reference above.
* `enable_dhcp` - See Argument Reference above.
* `dns_nameservers` - See Argument Reference above.
* `host_routes` - See Argument Reference above.

View File

@ -0,0 +1,68 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_objectstorage_container_v1"
sidebar_current: "docs-openstack-resource-objectstorage-container-v1"
description: |-
Manages a V1 container resource within OpenStack.
---
# openstack\_objectstorage\_container_v1
Manages a V1 container resource within OpenStack.
## Example Usage
```
resource "openstack_objectstorage_container_v1" "container_1" {
region = "RegionOne"
name = "tf-test-container-1"
metadata {
test = "true"
}
content_type = "application/json"
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to create the container. If
omitted, the `OS_REGION_NAME` environment variable is used. Changing this
creates a new container.
* `name` - (Required) A unique name for the container. Changing this creates a
new container.
* `container_read` - (Optional) Sets an access control list (ACL) that grants
read access. This header can contain a comma-delimited list of users that
can read the container (allows the GET method for all objects in the
container). Changing this updates the access control list read access.
* `container_sync_to` - (Optional) The destination for container synchronization.
Changing this updates container synchronization.
* `container_sync_key` - (Optional) The secret key for container synchronization.
Changing this updates container synchronization.
* `container_write` - (Optional) Sets an ACL that grants write access.
Changing this updates the access control list write access.
* `metadata` - (Optional) Custom key/value pairs to associate with the container.
Changing this updates the existing container metadata.
* `content_type` - (Optional) The MIME type for the container. Changing this
updates the MIME type.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `name` - See Argument Reference above.
* `container_read` - See Argument Reference above.
* `container_sync_to` - See Argument Reference above.
* `container_sync_key` - See Argument Reference above.
* `container_write` - See Argument Reference above.
* `metadata` - See Argument Reference above.
* `content_type` - See Argument Reference above.

View File

@ -0,0 +1,53 @@
<% wrap_layout :inner do %>
<% content_for :sidebar do %>
<div class="docs-sidebar hidden-print affix-top" role="complementary">
<ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>>
<a href="/docs/index.html">&laquo; Documentation Home</a>
</li>
<li<%= sidebar_current("docs-openstack-index") %>>
<a href="/docs/providers/openstack/index.html">OpenStack Provider</a>
</li>
<li<%= sidebar_current("docs-openstack-resource") %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-openstack-resource-blockstorage-volume-v1") %>>
<a href="/docs/providers/openstack/r/blockstorage_volume_v1.html">openstack_blockstorage_volume_v1</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-compute-instance-v2") %>>
<a href="/docs/providers/openstack/r/compute_instance_v2.html">openstack_compute_instance_v2</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-compute-keypair-v2") %>>
<a href="/docs/providers/openstack/r/compute_keypair_v2.html">openstack_compute_keypair_v2</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-compute-secgroup-v2") %>>
<a href="/docs/providers/openstack/r/compute_secgroup_v2.html">openstack_compute_secgroup_v2</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-lb-monitor-v1") %>>
<a href="/docs/providers/openstack/r/lb_monitor_v1.html">openstack_lb_monitor_v1</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-lb-pool-v1") %>>
<a href="/docs/providers/openstack/r/lb_pool_v1.html">openstack_lb_pool_v1</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-lb-vip-v1") %>>
<a href="/docs/providers/openstack/r/lb_vip_v1.html">openstack_lb_vip_v1</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-networking-network-v2") %>>
<a href="/docs/providers/openstack/r/networking_network_v2.html">openstack_networking_network_v2</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-networking-subnet-v2") %>>
<a href="/docs/providers/openstack/r/networking_subnet_v2.html">openstack_networking_subnet_v2</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-objectstorage-container-v1") %>>
<a href="/docs/providers/openstack/r/objectstorage_container_v1.html">openstack_objectstorage_container_v1</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %>
<% end %>