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