Merge pull request #924 from jrperritt/openstack-gophercloud-v1.0
OpenStack Provider
This commit is contained in:
commit
08814a51ba
|
@ -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,
|
||||
})
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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())
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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())
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}`)
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}`)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
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).
|
||||
|
||||
Use the navigation to the left to read about the available providers.
|
||||
|
|
|
@ -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)
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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">« 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 %>
|
Loading…
Reference in New Issue