Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenStack LBaaS v2 Support #7012

Merged
merged 2 commits into from
Jun 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions builtin/providers/openstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ func Provider() terraform.ResourceProvider {
"openstack_lb_monitor_v1": resourceLBMonitorV1(),
"openstack_lb_pool_v1": resourceLBPoolV1(),
"openstack_lb_vip_v1": resourceLBVipV1(),
"openstack_lb_loadbalancer_v2": resourceLoadBalancerV2(),
"openstack_lb_listener_v2": resourceListenerV2(),
"openstack_lb_pool_v2": resourcePoolV2(),
"openstack_lb_member_v2": resourceMemberV2(),
"openstack_lb_monitor_v2": resourceMonitorV2(),
"openstack_networking_network_v2": resourceNetworkingNetworkV2(),
"openstack_networking_subnet_v2": resourceNetworkingSubnetV2(),
"openstack_networking_floatingip_v2": resourceNetworkingFloatingIPV2(),
Expand Down
313 changes: 313 additions & 0 deletions builtin/providers/openstack/resource_openstack_lb_listener_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
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_v2/listeners"
)

func resourceListenerV2() *schema.Resource {
return &schema.Resource{
Create: resourceListenerV2Create,
Read: resourceListenerV2Read,
Update: resourceListenerV2Update,
Delete: resourceListenerV2Delete,

Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
},

"protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value != "TCP" && value != "HTTP" && value != "HTTPS" {
errors = append(errors, fmt.Errorf(
"Only 'TCP', 'HTTP', and 'HTTPS' are supported values for 'protocol'"))
}
return
},
},

"protocol_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},

"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},

"loadbalancer_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"default_pool_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},

"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"connection_limit": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},

"default_tls_container_ref": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"sni_container_refs": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"admin_state_up": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},

"id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
}
}

func resourceListenerV2Create(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)
connLimit := d.Get("connection_limit").(int)
var sniContainerRefs []string
if raw, ok := d.GetOk("sni_container_refs"); ok {
for _, v := range raw.([]interface{}) {
sniContainerRefs = append(sniContainerRefs, v.(string))
}
}
createOpts := listeners.CreateOpts{
Protocol: listeners.Protocol(d.Get("protocol").(string)),
ProtocolPort: d.Get("protocol_port").(int),
TenantID: d.Get("tenant_id").(string),
LoadbalancerID: d.Get("loadbalancer_id").(string),
Name: d.Get("name").(string),
DefaultPoolID: d.Get("default_pool_id").(string),
Description: d.Get("description").(string),
ConnLimit: &connLimit,
DefaultTlsContainerRef: d.Get("default_tls_container_ref").(string),
SniContainerRefs: sniContainerRefs,
AdminStateUp: &adminStateUp,
}

log.Printf("[DEBUG] Create Options: %#v", createOpts)
listener, err := listeners.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack LBaaSV2 listener: %s", err)
}
log.Printf("[INFO] Listener ID: %s", listener.ID)

log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 listener (%s) to become available.", listener.ID)

stateConf := &resource.StateChangeConf{
Pending: []string{"PENDING_CREATE"},
Target: []string{"ACTIVE"},
Refresh: waitForListenerActive(networkingClient, listener.ID),
Timeout: 2 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}

_, err = stateConf.WaitForState()
if err != nil {
return err
}

d.SetId(listener.ID)

return resourceListenerV2Read(d, meta)
}

func resourceListenerV2Read(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)
}

listener, err := listeners.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "LBV2 listener")
}

log.Printf("[DEBUG] Retreived OpenStack LBaaSV2 listener %s: %+v", d.Id(), listener)

d.Set("id", listener.ID)
d.Set("name", listener.Name)
d.Set("protocol", listener.Protocol)
d.Set("tenant_id", listener.TenantID)
d.Set("description", listener.Description)
d.Set("protocol_port", listener.ProtocolPort)
d.Set("admin_state_up", listener.AdminStateUp)
d.Set("default_pool_id", listener.DefaultPoolID)
d.Set("connection_limit", listener.ConnLimit)
d.Set("sni_container_refs", listener.SniContainerRefs)
d.Set("default_tls_container_ref", listener.DefaultTlsContainerRef)

return nil
}

func resourceListenerV2Update(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 listeners.UpdateOpts
if d.HasChange("name") {
updateOpts.Name = d.Get("name").(string)
}
if d.HasChange("description") {
updateOpts.Description = d.Get("description").(string)
}
if d.HasChange("connection_limit") {
connLimit := d.Get("connection_limit").(int)
updateOpts.ConnLimit = &connLimit
}
if d.HasChange("default_tls_container_ref") {
updateOpts.DefaultTlsContainerRef = d.Get("default_tls_container_ref").(string)
}
if d.HasChange("sni_container_refs") {
var sniContainerRefs []string
if raw, ok := d.GetOk("sni_container_refs"); ok {
for _, v := range raw.([]interface{}) {
sniContainerRefs = append(sniContainerRefs, v.(string))
}
}
updateOpts.SniContainerRefs = sniContainerRefs
}
if d.HasChange("admin_state_up") {
asu := d.Get("admin_state_up").(bool)
updateOpts.AdminStateUp = &asu
}

log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Listener %s with options: %+v", d.Id(), updateOpts)

_, err = listeners.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack LBaaSV2 Listener: %s", err)
}

return resourceListenerV2Read(d, meta)

}

func resourceListenerV2Delete(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)
}

stateConf := &resource.StateChangeConf{
Pending: []string{"ACTIVE", "PENDING_DELETE"},
Target: []string{"DELETED"},
Refresh: waitForListenerDelete(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 LBaaSV2 listener: %s", err)
}

d.SetId("")
return nil
}

func waitForListenerActive(networkingClient *gophercloud.ServiceClient, listenerID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
listener, err := listeners.Get(networkingClient, listenerID).Extract()
if err != nil {
return nil, "", err
}

// The listener resource has no Status attribute, so a successful Get is the best we can do
log.Printf("[DEBUG] OpenStack LBaaSV2 listener: %+v", listener)
return listener, "ACTIVE", nil
}
}

func waitForListenerDelete(networkingClient *gophercloud.ServiceClient, listenerID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 listener %s", listenerID)

listener, err := listeners.Get(networkingClient, listenerID).Extract()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return listener, "ACTIVE", err
}
if errCode.Actual == 404 {
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 listener %s", listenerID)
return listener, "DELETED", nil
}
}

log.Printf("[DEBUG] Openstack LBaaSV2 listener: %+v", listener)
err = listeners.Delete(networkingClient, listenerID).ExtractErr()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return listener, "ACTIVE", err
}
if errCode.Actual == 404 {
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 listener %s", listenerID)
return listener, "DELETED", nil
}
}

log.Printf("[DEBUG] OpenStack LBaaSV2 listener %s still active.", listenerID)
return listener, "ACTIVE", nil
}
}
Loading