github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go (about)

     1  package openstack
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  
    13  	"github.com/gophercloud/gophercloud"
    14  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
    15  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
    16  	"github.com/gophercloud/gophercloud/pagination"
    17  )
    18  
    19  func resourceLBPoolV1() *schema.Resource {
    20  	return &schema.Resource{
    21  		Create: resourceLBPoolV1Create,
    22  		Read:   resourceLBPoolV1Read,
    23  		Update: resourceLBPoolV1Update,
    24  		Delete: resourceLBPoolV1Delete,
    25  		Importer: &schema.ResourceImporter{
    26  			State: schema.ImportStatePassthrough,
    27  		},
    28  
    29  		Schema: map[string]*schema.Schema{
    30  			"region": &schema.Schema{
    31  				Type:        schema.TypeString,
    32  				Required:    true,
    33  				ForceNew:    true,
    34  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    35  			},
    36  			"name": &schema.Schema{
    37  				Type:     schema.TypeString,
    38  				Required: true,
    39  				ForceNew: false,
    40  			},
    41  			"protocol": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Required: true,
    44  				ForceNew: true,
    45  			},
    46  			"subnet_id": &schema.Schema{
    47  				Type:     schema.TypeString,
    48  				Required: true,
    49  				ForceNew: true,
    50  			},
    51  
    52  			"lb_method": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Required: true,
    55  				ForceNew: false,
    56  			},
    57  			"lb_provider": &schema.Schema{
    58  				Type:     schema.TypeString,
    59  				Optional: true,
    60  				Computed: true,
    61  				ForceNew: true,
    62  			},
    63  			"tenant_id": &schema.Schema{
    64  				Type:     schema.TypeString,
    65  				Optional: true,
    66  				ForceNew: true,
    67  				Computed: true,
    68  			},
    69  			"member": &schema.Schema{
    70  				Type:       schema.TypeSet,
    71  				Deprecated: "Use openstack_lb_member_v1 instead. This attribute will be removed in a future version.",
    72  				Optional:   true,
    73  				Elem: &schema.Resource{
    74  					Schema: map[string]*schema.Schema{
    75  						"region": &schema.Schema{
    76  							Type:        schema.TypeString,
    77  							Required:    true,
    78  							ForceNew:    true,
    79  							DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    80  						},
    81  						"tenant_id": &schema.Schema{
    82  							Type:     schema.TypeString,
    83  							Optional: true,
    84  							ForceNew: true,
    85  						},
    86  						"address": &schema.Schema{
    87  							Type:     schema.TypeString,
    88  							Required: true,
    89  							ForceNew: true,
    90  						},
    91  						"port": &schema.Schema{
    92  							Type:     schema.TypeInt,
    93  							Required: true,
    94  							ForceNew: true,
    95  						},
    96  						"admin_state_up": &schema.Schema{
    97  							Type:     schema.TypeBool,
    98  							Required: true,
    99  							ForceNew: false,
   100  						},
   101  					},
   102  				},
   103  				Set: resourceLBMemberV1Hash,
   104  			},
   105  			"monitor_ids": &schema.Schema{
   106  				Type:     schema.TypeSet,
   107  				Optional: true,
   108  				ForceNew: false,
   109  				Elem:     &schema.Schema{Type: schema.TypeString},
   110  				Set:      schema.HashString,
   111  			},
   112  		},
   113  	}
   114  }
   115  
   116  func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error {
   117  	config := meta.(*Config)
   118  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   119  	if err != nil {
   120  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   121  	}
   122  
   123  	createOpts := pools.CreateOpts{
   124  		Name:     d.Get("name").(string),
   125  		SubnetID: d.Get("subnet_id").(string),
   126  		TenantID: d.Get("tenant_id").(string),
   127  		Provider: d.Get("lb_provider").(string),
   128  	}
   129  
   130  	if v, ok := d.GetOk("protocol"); ok {
   131  		protocol := resourceLBPoolV1DetermineProtocol(v.(string))
   132  		createOpts.Protocol = protocol
   133  	}
   134  
   135  	if v, ok := d.GetOk("lb_method"); ok {
   136  		lbMethod := resourceLBPoolV1DetermineLBMethod(v.(string))
   137  		createOpts.LBMethod = lbMethod
   138  	}
   139  
   140  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   141  	p, err := pools.Create(networkingClient, createOpts).Extract()
   142  	if err != nil {
   143  		return fmt.Errorf("Error creating OpenStack LB pool: %s", err)
   144  	}
   145  	log.Printf("[INFO] LB Pool ID: %s", p.ID)
   146  
   147  	log.Printf("[DEBUG] Waiting for OpenStack LB pool (%s) to become available.", p.ID)
   148  
   149  	stateConf := &resource.StateChangeConf{
   150  		Pending:    []string{"PENDING_CREATE"},
   151  		Target:     []string{"ACTIVE"},
   152  		Refresh:    waitForLBPoolActive(networkingClient, p.ID),
   153  		Timeout:    2 * time.Minute,
   154  		Delay:      5 * time.Second,
   155  		MinTimeout: 3 * time.Second,
   156  	}
   157  
   158  	_, err = stateConf.WaitForState()
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	d.SetId(p.ID)
   164  
   165  	if mIDs := resourcePoolMonitorIDsV1(d); mIDs != nil {
   166  		for _, mID := range mIDs {
   167  			_, err := pools.AssociateMonitor(networkingClient, p.ID, mID).Extract()
   168  			if err != nil {
   169  				return fmt.Errorf("Error associating monitor (%s) with OpenStack LB pool (%s): %s", mID, p.ID, err)
   170  			}
   171  		}
   172  	}
   173  
   174  	if memberOpts := resourcePoolMembersV1(d); memberOpts != nil {
   175  		for _, memberOpt := range memberOpts {
   176  			_, err := members.Create(networkingClient, memberOpt).Extract()
   177  			if err != nil {
   178  				return fmt.Errorf("Error creating OpenStack LB member: %s", err)
   179  			}
   180  		}
   181  	}
   182  
   183  	return resourceLBPoolV1Read(d, meta)
   184  }
   185  
   186  func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error {
   187  	config := meta.(*Config)
   188  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   189  	if err != nil {
   190  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   191  	}
   192  
   193  	p, err := pools.Get(networkingClient, d.Id()).Extract()
   194  	if err != nil {
   195  		return CheckDeleted(d, err, "LB pool")
   196  	}
   197  
   198  	log.Printf("[DEBUG] Retrieved OpenStack LB Pool %s: %+v", d.Id(), p)
   199  
   200  	d.Set("name", p.Name)
   201  	d.Set("protocol", p.Protocol)
   202  	d.Set("subnet_id", p.SubnetID)
   203  	d.Set("lb_method", p.LBMethod)
   204  	d.Set("lb_provider", p.Provider)
   205  	d.Set("tenant_id", p.TenantID)
   206  	d.Set("monitor_ids", p.MonitorIDs)
   207  	d.Set("member_ids", p.MemberIDs)
   208  
   209  	return nil
   210  }
   211  
   212  func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error {
   213  	config := meta.(*Config)
   214  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   215  	if err != nil {
   216  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   217  	}
   218  
   219  	var updateOpts pools.UpdateOpts
   220  	// If either option changed, update both.
   221  	// Gophercloud complains if one is empty.
   222  	if d.HasChange("name") || d.HasChange("lb_method") {
   223  		updateOpts.Name = d.Get("name").(string)
   224  
   225  		lbMethod := resourceLBPoolV1DetermineLBMethod(d.Get("lb_method").(string))
   226  		updateOpts.LBMethod = lbMethod
   227  	}
   228  
   229  	log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts)
   230  
   231  	_, err = pools.Update(networkingClient, d.Id(), updateOpts).Extract()
   232  	if err != nil {
   233  		return fmt.Errorf("Error updating OpenStack LB Pool: %s", err)
   234  	}
   235  
   236  	if d.HasChange("monitor_ids") {
   237  		oldMIDsRaw, newMIDsRaw := d.GetChange("security_groups")
   238  		oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set)
   239  		monitorsToAdd := newMIDsSet.Difference(oldMIDsSet)
   240  		monitorsToRemove := oldMIDsSet.Difference(newMIDsSet)
   241  
   242  		log.Printf("[DEBUG] Monitors to add: %v", monitorsToAdd)
   243  
   244  		log.Printf("[DEBUG] Monitors to remove: %v", monitorsToRemove)
   245  
   246  		for _, m := range monitorsToAdd.List() {
   247  			_, err := pools.AssociateMonitor(networkingClient, d.Id(), m.(string)).Extract()
   248  			if err != nil {
   249  				return fmt.Errorf("Error associating monitor (%s) with OpenStack server (%s): %s", m.(string), d.Id(), err)
   250  			}
   251  			log.Printf("[DEBUG] Associated monitor (%s) with pool (%s)", m.(string), d.Id())
   252  		}
   253  
   254  		for _, m := range monitorsToRemove.List() {
   255  			_, err := pools.DisassociateMonitor(networkingClient, d.Id(), m.(string)).Extract()
   256  			if err != nil {
   257  				return fmt.Errorf("Error disassociating monitor (%s) from OpenStack server (%s): %s", m.(string), d.Id(), err)
   258  			}
   259  			log.Printf("[DEBUG] Disassociated monitor (%s) from pool (%s)", m.(string), d.Id())
   260  		}
   261  	}
   262  
   263  	if d.HasChange("member") {
   264  		oldMembersRaw, newMembersRaw := d.GetChange("member")
   265  		oldMembersSet, newMembersSet := oldMembersRaw.(*schema.Set), newMembersRaw.(*schema.Set)
   266  		membersToAdd := newMembersSet.Difference(oldMembersSet)
   267  		membersToRemove := oldMembersSet.Difference(newMembersSet)
   268  
   269  		log.Printf("[DEBUG] Members to add: %v", membersToAdd)
   270  
   271  		log.Printf("[DEBUG] Members to remove: %v", membersToRemove)
   272  
   273  		for _, m := range membersToRemove.List() {
   274  			oldMember := resourcePoolMemberV1(d, m)
   275  			listOpts := members.ListOpts{
   276  				PoolID:       d.Id(),
   277  				Address:      oldMember.Address,
   278  				ProtocolPort: oldMember.ProtocolPort,
   279  			}
   280  			err = members.List(networkingClient, listOpts).EachPage(func(page pagination.Page) (bool, error) {
   281  				extractedMembers, err := members.ExtractMembers(page)
   282  				if err != nil {
   283  					return false, err
   284  				}
   285  				for _, member := range extractedMembers {
   286  					err := members.Delete(networkingClient, member.ID).ExtractErr()
   287  					if err != nil {
   288  						return false, fmt.Errorf("Error deleting member (%s) from OpenStack LB pool (%s): %s", member.ID, d.Id(), err)
   289  					}
   290  					log.Printf("[DEBUG] Deleted member (%s) from pool (%s)", member.ID, d.Id())
   291  				}
   292  				return true, nil
   293  			})
   294  		}
   295  
   296  		for _, m := range membersToAdd.List() {
   297  			createOpts := resourcePoolMemberV1(d, m)
   298  			newMember, err := members.Create(networkingClient, createOpts).Extract()
   299  			if err != nil {
   300  				return fmt.Errorf("Error creating LB member: %s", err)
   301  			}
   302  			log.Printf("[DEBUG] Created member (%s) in OpenStack LB pool (%s)", newMember.ID, d.Id())
   303  		}
   304  	}
   305  
   306  	return resourceLBPoolV1Read(d, meta)
   307  }
   308  
   309  func resourceLBPoolV1Delete(d *schema.ResourceData, meta interface{}) error {
   310  	config := meta.(*Config)
   311  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   312  	if err != nil {
   313  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   314  	}
   315  
   316  	// Make sure all monitors are disassociated first
   317  	if v, ok := d.GetOk("monitor_ids"); ok {
   318  		if monitorIDList, ok := v.([]interface{}); ok {
   319  			for _, monitorID := range monitorIDList {
   320  				mID := monitorID.(string)
   321  				log.Printf("[DEBUG] Attempting to disassociate monitor %s from pool %s", mID, d.Id())
   322  				if res := pools.DisassociateMonitor(networkingClient, d.Id(), mID); res.Err != nil {
   323  					return fmt.Errorf("Error disassociating monitor %s from pool %s: %s", mID, d.Id(), err)
   324  				}
   325  			}
   326  		}
   327  	}
   328  
   329  	stateConf := &resource.StateChangeConf{
   330  		Pending:    []string{"ACTIVE", "PENDING_DELETE"},
   331  		Target:     []string{"DELETED"},
   332  		Refresh:    waitForLBPoolDelete(networkingClient, d.Id()),
   333  		Timeout:    2 * time.Minute,
   334  		Delay:      5 * time.Second,
   335  		MinTimeout: 3 * time.Second,
   336  	}
   337  
   338  	_, err = stateConf.WaitForState()
   339  	if err != nil {
   340  		return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err)
   341  	}
   342  
   343  	d.SetId("")
   344  	return nil
   345  }
   346  
   347  func resourcePoolMonitorIDsV1(d *schema.ResourceData) []string {
   348  	mIDsRaw := d.Get("monitor_ids").(*schema.Set)
   349  	mIDs := make([]string, mIDsRaw.Len())
   350  	for i, raw := range mIDsRaw.List() {
   351  		mIDs[i] = raw.(string)
   352  	}
   353  	return mIDs
   354  }
   355  
   356  func resourcePoolMembersV1(d *schema.ResourceData) []members.CreateOpts {
   357  	memberOptsRaw := d.Get("member").(*schema.Set)
   358  	memberOpts := make([]members.CreateOpts, memberOptsRaw.Len())
   359  	for i, raw := range memberOptsRaw.List() {
   360  		rawMap := raw.(map[string]interface{})
   361  		memberOpts[i] = members.CreateOpts{
   362  			TenantID:     rawMap["tenant_id"].(string),
   363  			Address:      rawMap["address"].(string),
   364  			ProtocolPort: rawMap["port"].(int),
   365  			PoolID:       d.Id(),
   366  		}
   367  	}
   368  	return memberOpts
   369  }
   370  
   371  func resourcePoolMemberV1(d *schema.ResourceData, raw interface{}) members.CreateOpts {
   372  	rawMap := raw.(map[string]interface{})
   373  	return members.CreateOpts{
   374  		TenantID:     rawMap["tenant_id"].(string),
   375  		Address:      rawMap["address"].(string),
   376  		ProtocolPort: rawMap["port"].(int),
   377  		PoolID:       d.Id(),
   378  	}
   379  }
   380  
   381  func resourceLBMemberV1Hash(v interface{}) int {
   382  	var buf bytes.Buffer
   383  	m := v.(map[string]interface{})
   384  	buf.WriteString(fmt.Sprintf("%s-", m["region"].(string)))
   385  	buf.WriteString(fmt.Sprintf("%s-", m["tenant_id"].(string)))
   386  	buf.WriteString(fmt.Sprintf("%s-", m["address"].(string)))
   387  	buf.WriteString(fmt.Sprintf("%d-", m["port"].(int)))
   388  
   389  	return hashcode.String(buf.String())
   390  }
   391  
   392  func resourceLBPoolV1DetermineProtocol(v string) pools.LBProtocol {
   393  	var protocol pools.LBProtocol
   394  	switch v {
   395  	case "TCP":
   396  		protocol = pools.ProtocolTCP
   397  	case "HTTP":
   398  		protocol = pools.ProtocolHTTP
   399  	case "HTTPS":
   400  		protocol = pools.ProtocolHTTPS
   401  	}
   402  
   403  	return protocol
   404  }
   405  
   406  func resourceLBPoolV1DetermineLBMethod(v string) pools.LBMethod {
   407  	var lbMethod pools.LBMethod
   408  	switch v {
   409  	case "ROUND_ROBIN":
   410  		lbMethod = pools.LBMethodRoundRobin
   411  	case "LEAST_CONNECTIONS":
   412  		lbMethod = pools.LBMethodLeastConnections
   413  	}
   414  
   415  	return lbMethod
   416  }
   417  
   418  func waitForLBPoolActive(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc {
   419  	return func() (interface{}, string, error) {
   420  		p, err := pools.Get(networkingClient, poolId).Extract()
   421  		if err != nil {
   422  			return nil, "", err
   423  		}
   424  
   425  		log.Printf("[DEBUG] OpenStack LB Pool: %+v", p)
   426  		if p.Status == "ACTIVE" {
   427  			return p, "ACTIVE", nil
   428  		}
   429  
   430  		return p, p.Status, nil
   431  	}
   432  }
   433  
   434  func waitForLBPoolDelete(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc {
   435  	return func() (interface{}, string, error) {
   436  		log.Printf("[DEBUG] Attempting to delete OpenStack LB Pool %s", poolId)
   437  
   438  		p, err := pools.Get(networkingClient, poolId).Extract()
   439  		if err != nil {
   440  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   441  				log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId)
   442  				return p, "DELETED", nil
   443  			}
   444  			return p, "ACTIVE", err
   445  		}
   446  
   447  		log.Printf("[DEBUG] OpenStack LB Pool: %+v", p)
   448  		err = pools.Delete(networkingClient, poolId).ExtractErr()
   449  		if err != nil {
   450  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   451  				log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId)
   452  				return p, "DELETED", nil
   453  			}
   454  			return p, "ACTIVE", err
   455  		}
   456  
   457  		log.Printf("[DEBUG] OpenStack LB Pool %s still active.", poolId)
   458  		return p, "ACTIVE", nil
   459  	}
   460  
   461  }