github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/openstack/resource_openstack_lb_vip_v1.go (about)

     1  package openstack
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/hashicorp/terraform/helper/resource"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  	"github.com/rackspace/gophercloud"
    11  	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
    12  	"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
    13  )
    14  
    15  func resourceLBVipV1() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceLBVipV1Create,
    18  		Read:   resourceLBVipV1Read,
    19  		Update: resourceLBVipV1Update,
    20  		Delete: resourceLBVipV1Delete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"region": &schema.Schema{
    24  				Type:        schema.TypeString,
    25  				Required:    true,
    26  				ForceNew:    true,
    27  				DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"),
    28  			},
    29  			"name": &schema.Schema{
    30  				Type:     schema.TypeString,
    31  				Required: true,
    32  				ForceNew: false,
    33  			},
    34  			"subnet_id": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Required: true,
    37  				ForceNew: true,
    38  			},
    39  			"protocol": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  				ForceNew: true,
    43  			},
    44  			"port": &schema.Schema{
    45  				Type:     schema.TypeInt,
    46  				Required: true,
    47  				ForceNew: true,
    48  			},
    49  			"pool_id": &schema.Schema{
    50  				Type:     schema.TypeString,
    51  				Required: true,
    52  				ForceNew: false,
    53  			},
    54  			"tenant_id": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Optional: true,
    57  				Computed: true,
    58  				ForceNew: true,
    59  			},
    60  			"address": &schema.Schema{
    61  				Type:     schema.TypeString,
    62  				Optional: true,
    63  				Computed: true,
    64  				ForceNew: true,
    65  			},
    66  			"description": &schema.Schema{
    67  				Type:     schema.TypeString,
    68  				Optional: true,
    69  				Computed: true,
    70  				ForceNew: false,
    71  			},
    72  			"persistence": &schema.Schema{
    73  				Type:     schema.TypeMap,
    74  				Optional: true,
    75  				ForceNew: false,
    76  			},
    77  			"conn_limit": &schema.Schema{
    78  				Type:     schema.TypeInt,
    79  				Optional: true,
    80  				Computed: true,
    81  				ForceNew: false,
    82  			},
    83  			"port_id": &schema.Schema{
    84  				Type:     schema.TypeString,
    85  				Computed: true,
    86  				ForceNew: false,
    87  			},
    88  			"floating_ip": &schema.Schema{
    89  				Type:     schema.TypeString,
    90  				Optional: true,
    91  				ForceNew: false,
    92  			},
    93  			"admin_state_up": &schema.Schema{
    94  				Type:     schema.TypeBool,
    95  				Optional: true,
    96  				Computed: true,
    97  				ForceNew: false,
    98  			},
    99  		},
   100  	}
   101  }
   102  
   103  func resourceLBVipV1Create(d *schema.ResourceData, meta interface{}) error {
   104  	config := meta.(*Config)
   105  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   106  	if err != nil {
   107  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   108  	}
   109  
   110  	createOpts := vips.CreateOpts{
   111  		Name:         d.Get("name").(string),
   112  		SubnetID:     d.Get("subnet_id").(string),
   113  		Protocol:     d.Get("protocol").(string),
   114  		ProtocolPort: d.Get("port").(int),
   115  		PoolID:       d.Get("pool_id").(string),
   116  		TenantID:     d.Get("tenant_id").(string),
   117  		Address:      d.Get("address").(string),
   118  		Description:  d.Get("description").(string),
   119  		Persistence:  resourceVipPersistenceV1(d),
   120  		ConnLimit:    gophercloud.MaybeInt(d.Get("conn_limit").(int)),
   121  	}
   122  
   123  	asu := d.Get("admin_state_up").(bool)
   124  	createOpts.AdminStateUp = &asu
   125  
   126  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   127  	p, err := vips.Create(networkingClient, createOpts).Extract()
   128  	if err != nil {
   129  		return fmt.Errorf("Error creating OpenStack LB VIP: %s", err)
   130  	}
   131  	log.Printf("[INFO] LB VIP ID: %s", p.ID)
   132  
   133  	log.Printf("[DEBUG] Waiting for OpenStack LB VIP (%s) to become available.", p.ID)
   134  
   135  	stateConf := &resource.StateChangeConf{
   136  		Pending:    []string{"PENDING_CREATE"},
   137  		Target:     "ACTIVE",
   138  		Refresh:    waitForLBVIPActive(networkingClient, p.ID),
   139  		Timeout:    2 * time.Minute,
   140  		Delay:      5 * time.Second,
   141  		MinTimeout: 3 * time.Second,
   142  	}
   143  
   144  	_, err = stateConf.WaitForState()
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	floatingIP := d.Get("floating_ip").(string)
   150  	if floatingIP != "" {
   151  		lbVipV1AssignFloatingIP(floatingIP, p.PortID, networkingClient)
   152  	}
   153  
   154  	d.SetId(p.ID)
   155  
   156  	return resourceLBVipV1Read(d, meta)
   157  }
   158  
   159  func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error {
   160  	config := meta.(*Config)
   161  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   162  	if err != nil {
   163  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   164  	}
   165  
   166  	p, err := vips.Get(networkingClient, d.Id()).Extract()
   167  	if err != nil {
   168  		return CheckDeleted(d, err, "LB VIP")
   169  	}
   170  
   171  	log.Printf("[DEBUG] Retreived OpenStack LB VIP %s: %+v", d.Id(), p)
   172  
   173  	d.Set("name", p.Name)
   174  	d.Set("subnet_id", p.SubnetID)
   175  	d.Set("protocol", p.Protocol)
   176  	d.Set("port", p.ProtocolPort)
   177  	d.Set("pool_id", p.PoolID)
   178  	d.Set("port_id", p.PortID)
   179  	d.Set("tenant_id", p.TenantID)
   180  	d.Set("address", p.Address)
   181  	d.Set("description", p.Description)
   182  	d.Set("conn_limit", p.ConnLimit)
   183  	d.Set("admin_state_up", p.AdminStateUp)
   184  
   185  	return nil
   186  }
   187  
   188  func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error {
   189  	config := meta.(*Config)
   190  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   191  	if err != nil {
   192  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   193  	}
   194  
   195  	var updateOpts vips.UpdateOpts
   196  	if d.HasChange("name") {
   197  		updateOpts.Name = d.Get("name").(string)
   198  	}
   199  	if d.HasChange("pool_id") {
   200  		updateOpts.PoolID = d.Get("pool_id").(string)
   201  	}
   202  	if d.HasChange("description") {
   203  		updateOpts.Description = d.Get("description").(string)
   204  	}
   205  	if d.HasChange("persistence") {
   206  		updateOpts.Persistence = resourceVipPersistenceV1(d)
   207  	}
   208  	if d.HasChange("conn_limit") {
   209  		updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int))
   210  	}
   211  	if d.HasChange("floating_ip") {
   212  		portID := d.Get("port_id").(string)
   213  
   214  		// Searching for a floating IP assigned to the VIP
   215  		listOpts := floatingips.ListOpts{
   216  			PortID: portID,
   217  		}
   218  		page, err := floatingips.List(networkingClient, listOpts).AllPages()
   219  		if err != nil {
   220  			return err
   221  		}
   222  
   223  		fips, err := floatingips.ExtractFloatingIPs(page)
   224  		if err != nil {
   225  			return err
   226  		}
   227  
   228  		// If a floating IP is found we unassign it
   229  		if len(fips) == 1 {
   230  			updateOpts := floatingips.UpdateOpts{
   231  				PortID: "",
   232  			}
   233  			if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil {
   234  				return err
   235  			}
   236  		}
   237  
   238  		// Assign the updated floating IP
   239  		floatingIP := d.Get("floating_ip").(string)
   240  		if floatingIP != "" {
   241  			lbVipV1AssignFloatingIP(floatingIP, portID, networkingClient)
   242  		}
   243  	}
   244  	if d.HasChange("admin_state_up") {
   245  		asu := d.Get("admin_state_up").(bool)
   246  		updateOpts.AdminStateUp = &asu
   247  	}
   248  
   249  	log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts)
   250  
   251  	_, err = vips.Update(networkingClient, d.Id(), updateOpts).Extract()
   252  	if err != nil {
   253  		return fmt.Errorf("Error updating OpenStack LB VIP: %s", err)
   254  	}
   255  
   256  	return resourceLBVipV1Read(d, meta)
   257  }
   258  
   259  func resourceLBVipV1Delete(d *schema.ResourceData, meta interface{}) error {
   260  	config := meta.(*Config)
   261  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   262  	if err != nil {
   263  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   264  	}
   265  
   266  	stateConf := &resource.StateChangeConf{
   267  		Pending:    []string{"ACTIVE"},
   268  		Target:     "DELETED",
   269  		Refresh:    waitForLBVIPDelete(networkingClient, d.Id()),
   270  		Timeout:    2 * time.Minute,
   271  		Delay:      5 * time.Second,
   272  		MinTimeout: 3 * time.Second,
   273  	}
   274  
   275  	_, err = stateConf.WaitForState()
   276  	if err != nil {
   277  		return fmt.Errorf("Error deleting OpenStack LB VIP: %s", err)
   278  	}
   279  
   280  	d.SetId("")
   281  	return nil
   282  }
   283  
   284  func resourceVipPersistenceV1(d *schema.ResourceData) *vips.SessionPersistence {
   285  	rawP := d.Get("persistence").(interface{})
   286  	rawMap := rawP.(map[string]interface{})
   287  	if len(rawMap) != 0 {
   288  		p := vips.SessionPersistence{}
   289  		if t, ok := rawMap["type"]; ok {
   290  			p.Type = t.(string)
   291  		}
   292  		if c, ok := rawMap["cookie_name"]; ok {
   293  			p.CookieName = c.(string)
   294  		}
   295  		return &p
   296  	}
   297  	return nil
   298  }
   299  
   300  func lbVipV1AssignFloatingIP(floatingIP, portID string, networkingClient *gophercloud.ServiceClient) error {
   301  	log.Printf("[DEBUG] Assigning floating IP %s to VIP %s", floatingIP, portID)
   302  
   303  	listOpts := floatingips.ListOpts{
   304  		FloatingIP: floatingIP,
   305  	}
   306  	page, err := floatingips.List(networkingClient, listOpts).AllPages()
   307  	if err != nil {
   308  		return err
   309  	}
   310  
   311  	fips, err := floatingips.ExtractFloatingIPs(page)
   312  	if err != nil {
   313  		return err
   314  	}
   315  	if len(fips) != 1 {
   316  		return fmt.Errorf("Unable to retrieve floating IP '%s'", floatingIP)
   317  	}
   318  
   319  	updateOpts := floatingips.UpdateOpts{
   320  		PortID: portID,
   321  	}
   322  	if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil {
   323  		return err
   324  	}
   325  
   326  	return nil
   327  }
   328  
   329  func waitForLBVIPActive(networkingClient *gophercloud.ServiceClient, vipId string) resource.StateRefreshFunc {
   330  	return func() (interface{}, string, error) {
   331  		p, err := vips.Get(networkingClient, vipId).Extract()
   332  		if err != nil {
   333  			return nil, "", err
   334  		}
   335  
   336  		log.Printf("[DEBUG] OpenStack LB VIP: %+v", p)
   337  		if p.Status == "ACTIVE" {
   338  			return p, "ACTIVE", nil
   339  		}
   340  
   341  		return p, p.Status, nil
   342  	}
   343  }
   344  
   345  func waitForLBVIPDelete(networkingClient *gophercloud.ServiceClient, vipId string) resource.StateRefreshFunc {
   346  	return func() (interface{}, string, error) {
   347  		log.Printf("[DEBUG] Attempting to delete OpenStack LB VIP %s", vipId)
   348  
   349  		p, err := vips.Get(networkingClient, vipId).Extract()
   350  		if err != nil {
   351  			errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
   352  			if !ok {
   353  				return p, "ACTIVE", err
   354  			}
   355  			if errCode.Actual == 404 {
   356  				log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId)
   357  				return p, "DELETED", nil
   358  			}
   359  		}
   360  
   361  		log.Printf("[DEBUG] OpenStack LB VIP: %+v", p)
   362  		err = vips.Delete(networkingClient, vipId).ExtractErr()
   363  		if err != nil {
   364  			errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
   365  			if !ok {
   366  				return p, "ACTIVE", err
   367  			}
   368  			if errCode.Actual == 404 {
   369  				log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId)
   370  				return p, "DELETED", nil
   371  			}
   372  		}
   373  
   374  		log.Printf("[DEBUG] OpenStack LB VIP %s still active.", vipId)
   375  		return p, "ACTIVE", nil
   376  	}
   377  
   378  }