provider/openstack: Load Balancing Member Resource

This commit adds the openstack_lb_member_v1 resource. This resource models a
load balancing member which was previously coupled to the openstack_lb_pool_v1
resource.

By creating an actual member resource, load balancing members can now be
dynamically managed through terraform.
This commit is contained in:
Joe Topjian 2015-12-17 07:20:36 +00:00
parent 4716451617
commit 8d5a2d05a4
7 changed files with 515 additions and 16 deletions

View File

@ -78,6 +78,7 @@ func Provider() terraform.ResourceProvider {
"openstack_fw_firewall_v1": resourceFWFirewallV1(), "openstack_fw_firewall_v1": resourceFWFirewallV1(),
"openstack_fw_policy_v1": resourceFWPolicyV1(), "openstack_fw_policy_v1": resourceFWPolicyV1(),
"openstack_fw_rule_v1": resourceFWRuleV1(), "openstack_fw_rule_v1": resourceFWRuleV1(),
"openstack_lb_member_v1": resourceLBMemberV1(),
"openstack_lb_monitor_v1": resourceLBMonitorV1(), "openstack_lb_monitor_v1": resourceLBMonitorV1(),
"openstack_lb_pool_v1": resourceLBPoolV1(), "openstack_lb_pool_v1": resourceLBPoolV1(),
"openstack_lb_vip_v1": resourceLBVipV1(), "openstack_lb_vip_v1": resourceLBVipV1(),

View File

@ -0,0 +1,214 @@
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/lbaas/members"
)
func resourceLBMemberV1() *schema.Resource {
return &schema.Resource{
Create: resourceLBMemberV1Create,
Read: resourceLBMemberV1Read,
Update: resourceLBMemberV1Update,
Delete: resourceLBMemberV1Delete,
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"),
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"pool_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"address": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"weight": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"admin_state_up": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
Computed: true,
},
},
}
}
func resourceLBMemberV1Create(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 := members.CreateOpts{
TenantID: d.Get("tenant_id").(string),
PoolID: d.Get("pool_id").(string),
Address: d.Get("address").(string),
ProtocolPort: d.Get("port").(int),
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
m, err := members.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack LB member: %s", err)
}
log.Printf("[INFO] LB member ID: %s", m.ID)
log.Printf("[DEBUG] Waiting for OpenStack LB member (%s) to become available.", m.ID)
stateConf := &resource.StateChangeConf{
Target: "ACTIVE",
Refresh: waitForLBMemberActive(networkingClient, m.ID),
Timeout: 2 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return err
}
d.SetId(m.ID)
return resourceLBMemberV1Read(d, meta)
}
func resourceLBMemberV1Read(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 := members.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "LB member")
}
log.Printf("[DEBUG] Retreived OpenStack LB member %s: %+v", d.Id(), m)
d.Set("weight", m.Weight)
d.Set("admin_state_up", m.AdminStateUp)
return nil
}
func resourceLBMemberV1Update(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 members.UpdateOpts
if d.HasChange("admin_state_up") {
asu := d.Get("admin_state_up").(bool)
updateOpts.AdminStateUp = asu
}
log.Printf("[DEBUG] Updating LB member %s with options: %+v", d.Id(), updateOpts)
_, err = members.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack LB member: %s", err)
}
return resourceLBMemberV1Read(d, meta)
}
func resourceLBMemberV1Delete(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 = members.Delete(networkingClient, d.Id()).ExtractErr()
if err != nil {
CheckDeleted(d, err, "LB member")
}
stateConf := &resource.StateChangeConf{
Pending: []string{"ACTIVE"},
Target: "DELETED",
Refresh: waitForLBMemberDelete(networkingClient, d.Id()),
Timeout: 2 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("Error deleting OpenStack LB member: %s", err)
}
d.SetId("")
return nil
}
func waitForLBMemberActive(networkingClient *gophercloud.ServiceClient, memberId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
m, err := members.Get(networkingClient, memberId).Extract()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] OpenStack LB member: %+v", m)
if m.Status == "ACTIVE" {
return m, "ACTIVE", nil
}
return m, m.Status, nil
}
}
func waitForLBMemberDelete(networkingClient *gophercloud.ServiceClient, memberId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
log.Printf("[DEBUG] Attempting to delete OpenStack LB member %s", memberId)
m, err := members.Get(networkingClient, memberId).Extract()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return m, "ACTIVE", err
}
if errCode.Actual == 404 {
log.Printf("[DEBUG] Successfully deleted OpenStack LB member %s", memberId)
return m, "DELETED", nil
}
}
log.Printf("[DEBUG] OpenStack LB member %s still active.", memberId)
return m, "ACTIVE", nil
}
}

View File

@ -0,0 +1,138 @@
package openstack
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
)
func TestAccLBV1Member_basic(t *testing.T) {
var member members.Member
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLBV1MemberDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccLBV1Member_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckLBV1MemberExists(t, "openstack_lb_member_v1.member_1", &member),
),
},
resource.TestStep{
Config: testAccLBV1Member_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_lb_member_v1.member_1", "admin_state_up", "false"),
),
},
},
})
}
func testAccCheckLBV1MemberDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckLBV1MemberDestroy) Error creating OpenStack networking client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_lb_member_v1" {
continue
}
_, err := members.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("LB Member still exists")
}
}
return nil
}
func testAccCheckLBV1MemberExists(t *testing.T, n string, member *members.Member) 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("(testAccCheckLBV1MemberExists) Error creating OpenStack networking client: %s", err)
}
found, err := members.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}
if found.ID != rs.Primary.ID {
return fmt.Errorf("Member not found")
}
*member = *found
return nil
}
}
var testAccLBV1Member_basic = fmt.Sprintf(`
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
}
resource "openstack_lb_pool_v1" "pool_1" {
name = "tf_test_lb_pool"
protocol = "HTTP"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
lb_method = "ROUND_ROBIN"
}
resource "openstack_lb_member_v1" "member_1" {
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
address = "192.168.199.10"
port = 80
}`)
var testAccLBV1Member_update = fmt.Sprintf(`
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
}
resource "openstack_lb_pool_v1" "pool_1" {
name = "tf_test_lb_pool"
protocol = "HTTP"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
lb_method = "ROUND_ROBIN"
}
resource "openstack_lb_member_v1" "member_1" {
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
address = "192.168.199.10"
port = 80
admin_state_up = false
}`)

View File

@ -231,18 +231,18 @@ var testAccLBV1Pool_fullstack = fmt.Sprintf(`
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
lb_method = "ROUND_ROBIN" lb_method = "ROUND_ROBIN"
monitor_ids = ["${openstack_lb_monitor_v1.monitor_1.id}"] monitor_ids = ["${openstack_lb_monitor_v1.monitor_1.id}"]
}
member { resource "openstack_lb_member_v1" "member_1" {
address = "${openstack_compute_instance_v2.instance_1.access_ip_v4}" pool_id = "${openstack_lb_pool_v1.pool_1.id}"
port = 80 address = "${openstack_compute_instance_v2.instance_1.access_ip_v4}"
admin_state_up = "true" port = 80
} }
member { resource "openstack_lb_member_v1" "member_2" {
address = "${openstack_compute_instance_v2.instance_2.access_ip_v4}" pool_id = "${openstack_lb_pool_v1.pool_1.id}"
port = 80 address = "${openstack_compute_instance_v2.instance_2.access_ip_v4}"
admin_state_up = "true" port = 80
}
} }
resource "openstack_lb_vip_v1" "vip_1" { resource "openstack_lb_vip_v1" "vip_1" {

View File

@ -0,0 +1,58 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_lb_member_v1"
sidebar_current: "docs-openstack-resource-lb-member-v1"
description: |-
Manages a V1 load balancer member resource within OpenStack.
---
# openstack\_lb\_member_v1
Manages a V1 load balancer member resource within OpenStack.
## Example Usage
```
resource "openstack_lb_member_v1" "member_1" {
pool_id = "d9415786-5f1a-428b-b35f-2f1523e146d2"
address = "192.168.0.10"
port = 80
}
```
## 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 member. If omitted, the
`OS_REGION_NAME` environment variable is used. Changing this creates a new
LB member.
* `pool_id` - (Required) The ID of the LB pool. Changing this creates a new
member.
* `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 member for another tenant. Changing this creates a new member.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `pool_id` - See Argument Reference above.
* `address` - See Argument Reference above.
* `port` - See Argument Reference above.
* `admin_state_up` - See Argument Reference above.
* `weight` - The load balancing weight of the member. This is currently unable
to be set through Terraform.

View File

@ -19,11 +19,92 @@ resource "openstack_lb_pool_v1" "pool_1" {
subnet_id = "12345" subnet_id = "12345"
lb_method = "ROUND_ROBIN" lb_method = "ROUND_ROBIN"
monitor_ids = ["67890"] monitor_ids = ["67890"]
member { }
address = "192.168.0.1" ```
port = 80
admin_state_up = "true" ## Complete Load Balancing Stack Example
```
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
}
resource "openstack_compute_secgroup_v2" "secgroup_1" {
name = "secgroup_1"
description = "Rules for secgroup_1"
rule {
from_port = -1
to_port = -1
ip_protocol = "icmp"
cidr = "0.0.0.0/0"
} }
rule {
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}
}
resource "openstack_compute_instance_v2" "instance_1" {
name = "instance_1"
security_groups = ["default", "${openstack_compute_secgroup_v2.secgroup_1.name}"]
network {
uuid = "${openstack_networking_network_v2.network_1.id}"
}
}
resource "openstack_compute_instance_v2" "instance_2" {
name = "instance_2"
security_groups = ["default", "${openstack_compute_secgroup_v2.secgroup_1.name}"]
network {
uuid = "${openstack_networking_network_v2.network_1.id}"
}
}
resource "openstack_lb_monitor_v1" "monitor_1" {
type = "TCP"
delay = 30
timeout = 5
max_retries = 3
admin_state_up = "true"
}
resource "openstack_lb_pool_v1" "pool_1" {
name = "pool_1"
protocol = "TCP"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
lb_method = "ROUND_ROBIN"
monitor_ids = ["${openstack_lb_monitor_v1.monitor_1.id}"]
}
resource "openstack_lb_member_v1" "member_1" {
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
address = "${openstack_compute_instance_v2.instance_1.access_ip_v4}"
port = 80
}
resource "openstack_lb_member_v1" "member_2" {
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
address = "${openstack_compute_instance_v2.instance_2.access_ip_v4}"
port = 80
}
resource "openstack_lb_vip_v1" "vip_1" {
name = "vip_1"
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
protocol = "TCP"
port = 80
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
} }
``` ```
@ -58,7 +139,8 @@ The following arguments are supported:
* `member` - (Optional) An existing node to add to the pool. Changing this * `member` - (Optional) An existing node to add to the pool. Changing this
updates the members of the pool. The member object structure is documented updates the members of the pool. The member object structure is documented
below. below. Please note that the `member` block is deprecated in favor of the
`openstack_lb_member_v1` resource.
The `member` block supports: The `member` block supports:
@ -75,7 +157,6 @@ state of the existing member.
* `tenant_id` - (Optional) The owner of the member. Required if admin wants to * `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. create a pool member for another tenant. Changing this creates a new member.
## Attributes Reference ## Attributes Reference
The following attributes are exported: The following attributes are exported:
@ -88,3 +169,7 @@ The following attributes are exported:
* `tenant_id` - See Argument Reference above. * `tenant_id` - See Argument Reference above.
* `monitor_id` - See Argument Reference above. * `monitor_id` - See Argument Reference above.
* `member` - See Argument Reference above. * `member` - See Argument Reference above.
## Notes
The `member` block is deprecated in favor of the `openstack_lb_member_v1` resource.

View File

@ -67,6 +67,9 @@
<li<%= sidebar_current(/^docs-openstack-resource-lb/) %>> <li<%= sidebar_current(/^docs-openstack-resource-lb/) %>>
<a href="#">Load Balancer Resources</a> <a href="#">Load Balancer Resources</a>
<ul class="nav nav-visible"> <ul class="nav nav-visible">
<li<%= sidebar_current("docs-openstack-resource-lb-member-v1") %>>
<a href="/docs/providers/openstack/r/lb_member_v1.html">openstack_lb_member_v1</a>
</li>
<li<%= sidebar_current("docs-openstack-resource-lb-monitor-v1") %>> <li<%= sidebar_current("docs-openstack-resource-lb-monitor-v1") %>>
<a href="/docs/providers/openstack/r/lb_monitor_v1.html">openstack_lb_monitor_v1</a> <a href="/docs/providers/openstack/r/lb_monitor_v1.html">openstack_lb_monitor_v1</a>
</li> </li>