github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/builtin/providers/openstack/resource_openstack_lb_loadbalancer_v2.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  
    11  	"github.com/gophercloud/gophercloud"
    12  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
    13  	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
    14  )
    15  
    16  func resourceLoadBalancerV2() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceLoadBalancerV2Create,
    19  		Read:   resourceLoadBalancerV2Read,
    20  		Update: resourceLoadBalancerV2Update,
    21  		Delete: resourceLoadBalancerV2Delete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"region": &schema.Schema{
    25  				Type:        schema.TypeString,
    26  				Required:    true,
    27  				ForceNew:    true,
    28  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    29  			},
    30  
    31  			"name": &schema.Schema{
    32  				Type:     schema.TypeString,
    33  				Optional: true,
    34  			},
    35  
    36  			"description": &schema.Schema{
    37  				Type:     schema.TypeString,
    38  				Optional: true,
    39  			},
    40  
    41  			"vip_subnet_id": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Required: true,
    44  				ForceNew: true,
    45  			},
    46  
    47  			"tenant_id": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				Computed: true,
    51  				ForceNew: true,
    52  			},
    53  
    54  			"vip_address": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Optional: true,
    57  				Computed: true,
    58  				ForceNew: true,
    59  			},
    60  
    61  			"vip_port_id": &schema.Schema{
    62  				Type:     schema.TypeString,
    63  				Computed: true,
    64  			},
    65  
    66  			"admin_state_up": &schema.Schema{
    67  				Type:     schema.TypeBool,
    68  				Default:  true,
    69  				Optional: true,
    70  			},
    71  
    72  			"flavor": &schema.Schema{
    73  				Type:     schema.TypeString,
    74  				Optional: true,
    75  				ForceNew: true,
    76  			},
    77  
    78  			"provider": &schema.Schema{
    79  				Type:     schema.TypeString,
    80  				Optional: true,
    81  				Computed: true,
    82  				ForceNew: true,
    83  			},
    84  
    85  			"security_group_ids": &schema.Schema{
    86  				Type:     schema.TypeSet,
    87  				Optional: true,
    88  				Elem:     &schema.Schema{Type: schema.TypeString},
    89  				Set:      schema.HashString,
    90  			},
    91  		},
    92  	}
    93  }
    94  
    95  func resourceLoadBalancerV2Create(d *schema.ResourceData, meta interface{}) error {
    96  	config := meta.(*Config)
    97  	networkingClient, err := config.networkingV2Client(GetRegion(d))
    98  	if err != nil {
    99  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   100  	}
   101  
   102  	adminStateUp := d.Get("admin_state_up").(bool)
   103  	createOpts := loadbalancers.CreateOpts{
   104  		Name:         d.Get("name").(string),
   105  		Description:  d.Get("description").(string),
   106  		VipSubnetID:  d.Get("vip_subnet_id").(string),
   107  		TenantID:     d.Get("tenant_id").(string),
   108  		VipAddress:   d.Get("vip_address").(string),
   109  		AdminStateUp: &adminStateUp,
   110  		Flavor:       d.Get("flavor").(string),
   111  		Provider:     d.Get("provider").(string),
   112  	}
   113  
   114  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   115  	lb, err := loadbalancers.Create(networkingClient, createOpts).Extract()
   116  	if err != nil {
   117  		return fmt.Errorf("Error creating OpenStack LoadBalancer: %s", err)
   118  	}
   119  	log.Printf("[INFO] LoadBalancer ID: %s", lb.ID)
   120  
   121  	log.Printf("[DEBUG] Waiting for Openstack LoadBalancer (%s) to become available.", lb.ID)
   122  
   123  	stateConf := &resource.StateChangeConf{
   124  		Pending:    []string{"PENDING_CREATE"},
   125  		Target:     []string{"ACTIVE"},
   126  		Refresh:    waitForLoadBalancerActive(networkingClient, lb.ID),
   127  		Timeout:    20 * time.Minute,
   128  		Delay:      5 * time.Second,
   129  		MinTimeout: 3 * time.Second,
   130  	}
   131  
   132  	_, err = stateConf.WaitForState()
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	// Once the loadbalancer has been created, apply any requested security groups
   138  	// to the port that was created behind the scenes.
   139  	if err := resourceLoadBalancerV2SecurityGroups(networkingClient, lb.VipPortID, d); err != nil {
   140  		return err
   141  	}
   142  
   143  	// If all has been successful, set the ID on the resource
   144  	d.SetId(lb.ID)
   145  
   146  	return resourceLoadBalancerV2Read(d, meta)
   147  }
   148  
   149  func resourceLoadBalancerV2Read(d *schema.ResourceData, meta interface{}) error {
   150  	config := meta.(*Config)
   151  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   152  	if err != nil {
   153  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   154  	}
   155  
   156  	lb, err := loadbalancers.Get(networkingClient, d.Id()).Extract()
   157  	if err != nil {
   158  		return CheckDeleted(d, err, "LoadBalancerV2")
   159  	}
   160  
   161  	log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 LoadBalancer %s: %+v", d.Id(), lb)
   162  
   163  	d.Set("name", lb.Name)
   164  	d.Set("description", lb.Description)
   165  	d.Set("vip_subnet_id", lb.VipSubnetID)
   166  	d.Set("tenant_id", lb.TenantID)
   167  	d.Set("vip_address", lb.VipAddress)
   168  	d.Set("vip_port_id", lb.VipPortID)
   169  	d.Set("admin_state_up", lb.AdminStateUp)
   170  	d.Set("flavor", lb.Flavor)
   171  	d.Set("provider", lb.Provider)
   172  
   173  	// Get any security groups on the VIP Port
   174  	if lb.VipPortID != "" {
   175  		port, err := ports.Get(networkingClient, lb.VipPortID).Extract()
   176  		if err != nil {
   177  			return err
   178  		}
   179  
   180  		d.Set("security_group_ids", port.SecurityGroups)
   181  	}
   182  
   183  	return nil
   184  }
   185  
   186  func resourceLoadBalancerV2Update(d *schema.ResourceData, meta interface{}) error {
   187  	config := meta.(*Config)
   188  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   189  	if err != nil {
   190  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   191  	}
   192  
   193  	var updateOpts loadbalancers.UpdateOpts
   194  	if d.HasChange("name") {
   195  		updateOpts.Name = d.Get("name").(string)
   196  	}
   197  	if d.HasChange("description") {
   198  		updateOpts.Description = d.Get("description").(string)
   199  	}
   200  	if d.HasChange("admin_state_up") {
   201  		asu := d.Get("admin_state_up").(bool)
   202  		updateOpts.AdminStateUp = &asu
   203  	}
   204  
   205  	log.Printf("[DEBUG] Updating OpenStack LBaaSV2 LoadBalancer %s with options: %+v", d.Id(), updateOpts)
   206  
   207  	_, err = loadbalancers.Update(networkingClient, d.Id(), updateOpts).Extract()
   208  	if err != nil {
   209  		return fmt.Errorf("Error updating OpenStack LBaaSV2 LoadBalancer: %s", err)
   210  	}
   211  
   212  	// Security Groups get updated separately
   213  	if d.HasChange("security_group_ids") {
   214  		vipPortID := d.Get("vip_port_id").(string)
   215  		if err := resourceLoadBalancerV2SecurityGroups(networkingClient, vipPortID, d); err != nil {
   216  			return err
   217  		}
   218  	}
   219  
   220  	return resourceLoadBalancerV2Read(d, meta)
   221  }
   222  
   223  func resourceLoadBalancerV2Delete(d *schema.ResourceData, meta interface{}) error {
   224  	config := meta.(*Config)
   225  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   226  	if err != nil {
   227  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   228  	}
   229  
   230  	stateConf := &resource.StateChangeConf{
   231  		Pending:    []string{"ACTIVE", "PENDING_DELETE"},
   232  		Target:     []string{"DELETED"},
   233  		Refresh:    waitForLoadBalancerDelete(networkingClient, d.Id()),
   234  		Timeout:    2 * time.Minute,
   235  		Delay:      5 * time.Second,
   236  		MinTimeout: 3 * time.Second,
   237  	}
   238  
   239  	_, err = stateConf.WaitForState()
   240  	if err != nil {
   241  		return fmt.Errorf("Error deleting OpenStack LBaaSV2 LoadBalancer: %s", err)
   242  	}
   243  
   244  	d.SetId("")
   245  	return nil
   246  }
   247  
   248  func resourceLoadBalancerV2SecurityGroups(networkingClient *gophercloud.ServiceClient, vipPortID string, d *schema.ResourceData) error {
   249  	if vipPortID != "" {
   250  		if _, ok := d.GetOk("security_group_ids"); ok {
   251  			updateOpts := ports.UpdateOpts{
   252  				SecurityGroups: resourcePortSecurityGroupsV2(d),
   253  			}
   254  
   255  			log.Printf("[DEBUG] Adding security groups to OpenStack LoadBalancer "+
   256  				"VIP Port (%s): %#v", vipPortID, updateOpts)
   257  
   258  			_, err := ports.Update(networkingClient, vipPortID, updateOpts).Extract()
   259  			if err != nil {
   260  				return err
   261  			}
   262  		}
   263  	}
   264  
   265  	return nil
   266  }
   267  
   268  func waitForLoadBalancerActive(networkingClient *gophercloud.ServiceClient, lbID string) resource.StateRefreshFunc {
   269  	return func() (interface{}, string, error) {
   270  		lb, err := loadbalancers.Get(networkingClient, lbID).Extract()
   271  		if err != nil {
   272  			return nil, "", err
   273  		}
   274  
   275  		log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer: %+v", lb)
   276  		if lb.ProvisioningStatus == "ACTIVE" {
   277  			return lb, "ACTIVE", nil
   278  		}
   279  
   280  		return lb, lb.ProvisioningStatus, nil
   281  	}
   282  }
   283  
   284  func waitForLoadBalancerDelete(networkingClient *gophercloud.ServiceClient, lbID string) resource.StateRefreshFunc {
   285  	return func() (interface{}, string, error) {
   286  		log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 LoadBalancer %s", lbID)
   287  
   288  		lb, err := loadbalancers.Get(networkingClient, lbID).Extract()
   289  		if err != nil {
   290  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   291  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 LoadBalancer %s", lbID)
   292  				return lb, "DELETED", nil
   293  			}
   294  			return lb, "ACTIVE", err
   295  		}
   296  
   297  		log.Printf("[DEBUG] Openstack LoadBalancerV2: %+v", lb)
   298  		err = loadbalancers.Delete(networkingClient, lbID).ExtractErr()
   299  		if err != nil {
   300  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   301  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 LoadBalancer %s", lbID)
   302  				return lb, "DELETED", nil
   303  			}
   304  
   305  			if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
   306  				if errCode.Actual == 409 {
   307  					log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer (%s) is still in use.", lbID)
   308  					return lb, "ACTIVE", nil
   309  				}
   310  			}
   311  
   312  			return lb, "ACTIVE", err
   313  		}
   314  
   315  		log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer (%s) still active.", lbID)
   316  		return lb, "ACTIVE", nil
   317  	}
   318  }