terraform/builtin/providers/openstack/resource_openstack_lb_pool_...

407 lines
12 KiB
Go
Raw Normal View History

2015-01-14 19:55:17 +01:00
package openstack
import (
2015-02-01 01:19:42 +01:00
"bytes"
2015-01-14 19:55:17 +01:00
"fmt"
"log"
"time"
2015-01-14 19:55:17 +01:00
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
2015-01-26 23:45:05 +01:00
"github.com/hashicorp/terraform/helper/schema"
"github.com/rackspace/gophercloud"
2015-02-01 01:19:42 +01:00
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
2015-01-14 19:55:17 +01:00
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
2015-02-01 01:19:42 +01:00
"github.com/rackspace/gophercloud/pagination"
2015-01-14 19:55:17 +01:00
)
2015-01-26 19:09:27 +01:00
func resourceLBPoolV1() *schema.Resource {
2015-01-14 19:55:17 +01:00
return &schema.Resource{
2015-01-26 19:09:27 +01:00
Create: resourceLBPoolV1Create,
Read: resourceLBPoolV1Read,
Update: resourceLBPoolV1Update,
Delete: resourceLBPoolV1Delete,
2015-01-14 19:55:17 +01:00
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
},
2015-01-14 19:55:17 +01:00
"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,
2015-05-10 00:43:40 +02:00
Computed: true,
2015-01-14 19:55:17 +01:00
},
2015-02-01 01:19:42 +01:00
"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: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
2015-02-01 01:19:42 +01:00
},
"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,
},
2015-01-14 19:55:17 +01:00
"monitor_ids": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
2016-02-08 00:51:26 +01:00
Set: schema.HashString,
2015-01-14 19:55:17 +01:00
},
},
}
}
2015-01-26 19:09:27 +01:00
func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error {
2015-01-14 19:55:17 +01:00
config := meta.(*Config)
2015-01-31 22:33:54 +01:00
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
2015-01-14 19:55:17 +01:00
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),
}
2015-03-24 13:59:55 +01:00
log.Printf("[DEBUG] Create Options: %#v", createOpts)
p, err := pools.Create(networkingClient, createOpts).Extract()
2015-01-14 19:55:17 +01:00
if err != nil {
return fmt.Errorf("Error creating OpenStack LB pool: %s", err)
}
log.Printf("[INFO] LB Pool ID: %s", p.ID)
log.Printf("[DEBUG] Waiting for OpenStack LB pool (%s) to become available.", p.ID)
stateConf := &resource.StateChangeConf{
Pending: []string{"PENDING_CREATE"},
Target: []string{"ACTIVE"},
Refresh: waitForLBPoolActive(networkingClient, p.ID),
Timeout: 2 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return err
}
2015-01-14 19:55:17 +01:00
d.SetId(p.ID)
2015-01-26 19:09:27 +01:00
if mIDs := resourcePoolMonitorIDsV1(d); mIDs != nil {
2015-01-14 19:55:17 +01:00
for _, mID := range mIDs {
_, err := pools.AssociateMonitor(networkingClient, p.ID, mID).Extract()
2015-01-14 19:55:17 +01:00
if err != nil {
return fmt.Errorf("Error associating monitor (%s) with OpenStack LB pool (%s): %s", mID, p.ID, err)
}
}
}
2015-02-01 01:19:42 +01:00
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)
}
}
}
2015-01-26 19:09:27 +01:00
return resourceLBPoolV1Read(d, meta)
2015-01-14 19:55:17 +01:00
}
2015-01-26 19:09:27 +01:00
func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error {
2015-01-14 19:55:17 +01:00
config := meta.(*Config)
2015-01-31 22:33:54 +01:00
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
2015-01-14 19:55:17 +01:00
p, err := pools.Get(networkingClient, d.Id()).Extract()
2015-01-14 19:55:17 +01:00
if err != nil {
return CheckDeleted(d, err, "LB pool")
2015-01-14 19:55:17 +01:00
}
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)
2015-03-24 13:59:55 +01:00
d.Set("tenant_id", p.TenantID)
2015-01-14 19:55:17 +01:00
d.Set("monitor_ids", p.MonitorIDs)
2015-02-01 01:19:42 +01:00
d.Set("member_ids", p.MemberIDs)
2015-01-14 19:55:17 +01:00
return nil
}
2015-01-26 19:09:27 +01:00
func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error {
2015-01-14 19:55:17 +01:00
config := meta.(*Config)
2015-01-31 22:33:54 +01:00
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
2015-01-14 19:55:17 +01:00
var updateOpts pools.UpdateOpts
2015-05-10 00:43:40 +02:00
// If either option changed, update both.
// Gophercloud complains if one is empty.
if d.HasChange("name") || d.HasChange("lb_method") {
2015-01-14 19:55:17 +01:00
updateOpts.Name = d.Get("name").(string)
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()
2015-01-14 19:55:17 +01:00
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()
2015-01-14 19:55:17 +01:00
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()
2015-01-14 19:55:17 +01:00
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())
}
}
2015-02-01 01:19:42 +01:00
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())
}
}
2015-01-26 19:09:27 +01:00
return resourceLBPoolV1Read(d, meta)
2015-01-14 19:55:17 +01:00
}
2015-01-26 19:09:27 +01:00
func resourceLBPoolV1Delete(d *schema.ResourceData, meta interface{}) error {
2015-01-14 19:55:17 +01:00
config := meta.(*Config)
2015-01-31 22:33:54 +01:00
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
2015-01-14 19:55:17 +01:00
stateConf := &resource.StateChangeConf{
Pending: []string{"ACTIVE", "PENDING_DELETE"},
Target: []string{"DELETED"},
Refresh: waitForLBPoolDelete(networkingClient, d.Id()),
Timeout: 2 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
2015-01-14 19:55:17 +01:00
if err != nil {
return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err)
}
d.SetId("")
return nil
}
2015-01-26 19:09:27 +01:00
func resourcePoolMonitorIDsV1(d *schema.ResourceData) []string {
2015-01-14 19:55:17 +01:00
mIDsRaw := d.Get("monitor_ids").(*schema.Set)
mIDs := make([]string, mIDsRaw.Len())
for i, raw := range mIDsRaw.List() {
mIDs[i] = raw.(string)
}
return mIDs
}
2015-02-01 01:19:42 +01:00
func resourcePoolMembersV1(d *schema.ResourceData) []members.CreateOpts {
2015-10-08 14:48:04 +02:00
memberOptsRaw := d.Get("member").(*schema.Set)
2015-02-01 01:19:42 +01:00
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())
}
func waitForLBPoolActive(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
p, err := pools.Get(networkingClient, poolId).Extract()
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] OpenStack LB Pool: %+v", p)
if p.Status == "ACTIVE" {
return p, "ACTIVE", nil
}
return p, p.Status, nil
}
}
func waitForLBPoolDelete(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
log.Printf("[DEBUG] Attempting to delete OpenStack LB Pool %s", poolId)
p, err := pools.Get(networkingClient, poolId).Extract()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return p, "ACTIVE", err
}
if errCode.Actual == 404 {
log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId)
return p, "DELETED", nil
}
}
log.Printf("[DEBUG] OpenStack LB Pool: %+v", p)
err = pools.Delete(networkingClient, poolId).ExtractErr()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return p, "ACTIVE", err
}
if errCode.Actual == 404 {
log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId)
return p, "DELETED", nil
}
}
log.Printf("[DEBUG] OpenStack LB Pool %s still active.", poolId)
return p, "ACTIVE", nil
}
}