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