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