github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/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/gophercloud/gophercloud"
     9  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
    10  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    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] Retrieved 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  		v := d.Get("name").(string)
   211  		updateOpts.Name = &v
   212  	}
   213  
   214  	if d.HasChange("pool_id") {
   215  		v := d.Get("pool_id").(string)
   216  		updateOpts.PoolID = &v
   217  	}
   218  
   219  	if d.HasChange("description") {
   220  		v := d.Get("description").(string)
   221  		updateOpts.Description = &v
   222  	}
   223  
   224  	if d.HasChange("conn_limit") {
   225  		updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int))
   226  	}
   227  
   228  	if d.HasChange("floating_ip") {
   229  		portID := d.Get("port_id").(string)
   230  
   231  		// Searching for a floating IP assigned to the VIP
   232  		listOpts := floatingips.ListOpts{
   233  			PortID: portID,
   234  		}
   235  		page, err := floatingips.List(networkingClient, listOpts).AllPages()
   236  		if err != nil {
   237  			return err
   238  		}
   239  
   240  		fips, err := floatingips.ExtractFloatingIPs(page)
   241  		if err != nil {
   242  			return err
   243  		}
   244  
   245  		// If a floating IP is found we unassign it
   246  		if len(fips) == 1 {
   247  			portID := ""
   248  			updateOpts := floatingips.UpdateOpts{
   249  				PortID: &portID,
   250  			}
   251  			if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil {
   252  				return err
   253  			}
   254  		}
   255  
   256  		// Assign the updated floating IP
   257  		floatingIP := d.Get("floating_ip").(string)
   258  		if floatingIP != "" {
   259  			lbVipV1AssignFloatingIP(floatingIP, portID, networkingClient)
   260  		}
   261  	}
   262  
   263  	if d.HasChange("admin_state_up") {
   264  		asu := d.Get("admin_state_up").(bool)
   265  		updateOpts.AdminStateUp = &asu
   266  	}
   267  
   268  	// Persistence has to be included, even if it hasn't changed.
   269  	updateOpts.Persistence = resourceVipPersistenceV1(d)
   270  
   271  	log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts)
   272  
   273  	_, err = vips.Update(networkingClient, d.Id(), updateOpts).Extract()
   274  	if err != nil {
   275  		return fmt.Errorf("Error updating OpenStack LB VIP: %s", err)
   276  	}
   277  
   278  	return resourceLBVipV1Read(d, meta)
   279  }
   280  
   281  func resourceLBVipV1Delete(d *schema.ResourceData, meta interface{}) error {
   282  	config := meta.(*Config)
   283  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   284  	if err != nil {
   285  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   286  	}
   287  
   288  	stateConf := &resource.StateChangeConf{
   289  		Pending:    []string{"ACTIVE", "PENDING_DELETE"},
   290  		Target:     []string{"DELETED"},
   291  		Refresh:    waitForLBVIPDelete(networkingClient, d.Id()),
   292  		Timeout:    2 * time.Minute,
   293  		Delay:      5 * time.Second,
   294  		MinTimeout: 3 * time.Second,
   295  	}
   296  
   297  	_, err = stateConf.WaitForState()
   298  	if err != nil {
   299  		return fmt.Errorf("Error deleting OpenStack LB VIP: %s", err)
   300  	}
   301  
   302  	d.SetId("")
   303  	return nil
   304  }
   305  
   306  func resourceVipPersistenceV1(d *schema.ResourceData) *vips.SessionPersistence {
   307  	rawP := d.Get("persistence").(interface{})
   308  	rawMap := rawP.(map[string]interface{})
   309  	if len(rawMap) != 0 {
   310  		p := vips.SessionPersistence{}
   311  		if t, ok := rawMap["type"]; ok {
   312  			p.Type = t.(string)
   313  		}
   314  		if c, ok := rawMap["cookie_name"]; ok {
   315  			p.CookieName = c.(string)
   316  		}
   317  		return &p
   318  	}
   319  	return nil
   320  }
   321  
   322  func lbVipV1AssignFloatingIP(floatingIP, portID string, networkingClient *gophercloud.ServiceClient) error {
   323  	log.Printf("[DEBUG] Assigning floating IP %s to VIP %s", floatingIP, portID)
   324  
   325  	listOpts := floatingips.ListOpts{
   326  		FloatingIP: floatingIP,
   327  	}
   328  	page, err := floatingips.List(networkingClient, listOpts).AllPages()
   329  	if err != nil {
   330  		return err
   331  	}
   332  
   333  	fips, err := floatingips.ExtractFloatingIPs(page)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	if len(fips) != 1 {
   338  		return fmt.Errorf("Unable to retrieve floating IP '%s'", floatingIP)
   339  	}
   340  
   341  	updateOpts := floatingips.UpdateOpts{
   342  		PortID: &portID,
   343  	}
   344  	if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil {
   345  		return err
   346  	}
   347  
   348  	return nil
   349  }
   350  
   351  func waitForLBVIPActive(networkingClient *gophercloud.ServiceClient, vipId string) resource.StateRefreshFunc {
   352  	return func() (interface{}, string, error) {
   353  		p, err := vips.Get(networkingClient, vipId).Extract()
   354  		if err != nil {
   355  			return nil, "", err
   356  		}
   357  
   358  		log.Printf("[DEBUG] OpenStack LB VIP: %+v", p)
   359  		if p.Status == "ACTIVE" {
   360  			return p, "ACTIVE", nil
   361  		}
   362  
   363  		return p, p.Status, nil
   364  	}
   365  }
   366  
   367  func waitForLBVIPDelete(networkingClient *gophercloud.ServiceClient, vipId string) resource.StateRefreshFunc {
   368  	return func() (interface{}, string, error) {
   369  		log.Printf("[DEBUG] Attempting to delete OpenStack LB VIP %s", vipId)
   370  
   371  		p, err := vips.Get(networkingClient, vipId).Extract()
   372  		if err != nil {
   373  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   374  				log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId)
   375  				return p, "DELETED", nil
   376  			}
   377  			return p, "ACTIVE", err
   378  		}
   379  
   380  		log.Printf("[DEBUG] OpenStack LB VIP: %+v", p)
   381  		err = vips.Delete(networkingClient, vipId).ExtractErr()
   382  		if err != nil {
   383  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   384  				log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId)
   385  				return p, "DELETED", nil
   386  			}
   387  			return p, "ACTIVE", err
   388  		}
   389  
   390  		log.Printf("[DEBUG] OpenStack LB VIP %s still active.", vipId)
   391  		return p, "ACTIVE", nil
   392  	}
   393  
   394  }