diff --git a/builtin/providers/openstack/resource_openstack_lb_vip.go b/builtin/providers/openstack/resource_openstack_lb_vip.go new file mode 100644 index 000000000..405d01a19 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_lb_vip.go @@ -0,0 +1,256 @@ +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 resourceLBVip() *schema.Resource { + return &schema.Resource{ + Create: resourceLBVipCreate, + Read: resourceLBVipRead, + Update: resourceLBVipUpdate, + Delete: resourceLBVipDelete, + + Schema: map[string]*schema.Schema{ + "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.TypeString, + 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 resourceLBVipCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + 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: resourceVipPersistence(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("[INFO] Requesting lb vip creation") + p, err := vips.Create(osClient, 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 resourceLBVipRead(d, meta) +} + +func resourceLBVipRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + p, err := vips.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack LB VIP: %s", err) + } + + 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 _, exists := d.GetOk("tenant_id"); exists { + if d.HasChange("tenant_id") { + d.Set("tenant_id", p.TenantID) + } + } else { + d.Set("tenant_id", "") + } + + if _, exists := d.GetOk("address"); exists { + if d.HasChange("address") { + d.Set("address", p.Address) + } + } else { + d.Set("address", "") + } + + if _, exists := d.GetOk("description"); exists { + if d.HasChange("description") { + d.Set("description", p.Description) + } + } else { + d.Set("description", "") + } + + if _, exists := d.GetOk("persistence"); exists { + if d.HasChange("persistence") { + d.Set("persistence", p.Description) + } + } + + if _, exists := d.GetOk("conn_limit"); exists { + if d.HasChange("conn_limit") { + d.Set("conn_limit", p.ConnLimit) + } + } else { + d.Set("conn_limit", "") + } + + if _, exists := d.GetOk("admin_state_up"); exists { + if d.HasChange("admin_state_up") { + d.Set("admin_state_up", strconv.FormatBool(p.AdminStateUp)) + } + } else { + d.Set("admin_state_up", "") + } + + return nil +} + +func resourceLBVipUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + 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 = resourceVipPersistence(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(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack LB VIP: %s", err) + } + + return resourceLBVipRead(d, meta) +} + +func resourceLBVipDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + err := vips.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack LB VIP: %s", err) + } + + d.SetId("") + return nil +} + +func resourceVipPersistence(d *schema.ResourceData) *vips.SessionPersistence { + rawP := d.Get("persistence").(interface{}) + rawMap := rawP.(map[string]interface{}) + p := vips.SessionPersistence{ + Type: rawMap["type"].(string), + CookieName: rawMap["cookie_name"].(string), + } + return &p +} diff --git a/website/source/docs/providers/openstack/r/lb_vip.html.markdown b/website/source/docs/providers/openstack/r/lb_vip.html.markdown new file mode 100644 index 000000000..0eddcaa5f --- /dev/null +++ b/website/source/docs/providers/openstack/r/lb_vip.html.markdown @@ -0,0 +1,89 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_lb_vip" +sidebar_current: "docs-openstack-resource-lb-vip" +description: |- + Manages a load balancer vip resource within OpenStack. +--- + +# openstack\_lb\_vip + +Manages a load balancer vip resource within OpenStack. + +## Example Usage + +``` +resource "openstack_lb_vip" "vip_1" { + name = "tf_test_lb_vip" + subnet_id = "12345" + protocol = "HTTP" + port = 80 + pool_id = "67890" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `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: + +* `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.