github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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  				Deprecated: "Please use loadbalancer_provider",
    84  			},
    85  
    86  			"loadbalancer_provider": &schema.Schema{
    87  				Type:     schema.TypeString,
    88  				Optional: true,
    89  				Computed: true,
    90  				ForceNew: true,
    91  			},
    92  
    93  			"security_group_ids": &schema.Schema{
    94  				Type:     schema.TypeSet,
    95  				Optional: true,
    96  				Computed: true,
    97  				Elem:     &schema.Schema{Type: schema.TypeString},
    98  				Set:      schema.HashString,
    99  			},
   100  		},
   101  	}
   102  }
   103  
   104  func resourceLoadBalancerV2Create(d *schema.ResourceData, meta interface{}) error {
   105  	config := meta.(*Config)
   106  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   107  	if err != nil {
   108  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   109  	}
   110  
   111  	var lbProvider string
   112  	if v, ok := d.GetOk("loadbalancer_provider"); ok {
   113  		lbProvider = v.(string)
   114  	}
   115  
   116  	adminStateUp := d.Get("admin_state_up").(bool)
   117  	createOpts := loadbalancers.CreateOpts{
   118  		Name:         d.Get("name").(string),
   119  		Description:  d.Get("description").(string),
   120  		VipSubnetID:  d.Get("vip_subnet_id").(string),
   121  		TenantID:     d.Get("tenant_id").(string),
   122  		VipAddress:   d.Get("vip_address").(string),
   123  		AdminStateUp: &adminStateUp,
   124  		Flavor:       d.Get("flavor").(string),
   125  		Provider:     lbProvider,
   126  	}
   127  
   128  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   129  	lb, err := loadbalancers.Create(networkingClient, createOpts).Extract()
   130  	if err != nil {
   131  		return fmt.Errorf("Error creating OpenStack LoadBalancer: %s", err)
   132  	}
   133  	log.Printf("[INFO] LoadBalancer ID: %s", lb.ID)
   134  
   135  	log.Printf("[DEBUG] Waiting for Openstack LoadBalancer (%s) to become available.", lb.ID)
   136  
   137  	stateConf := &resource.StateChangeConf{
   138  		Pending:    []string{"PENDING_CREATE"},
   139  		Target:     []string{"ACTIVE"},
   140  		Refresh:    waitForLoadBalancerActive(networkingClient, lb.ID),
   141  		Timeout:    20 * time.Minute,
   142  		Delay:      5 * time.Second,
   143  		MinTimeout: 3 * time.Second,
   144  	}
   145  
   146  	_, err = stateConf.WaitForState()
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	// Once the loadbalancer has been created, apply any requested security groups
   152  	// to the port that was created behind the scenes.
   153  	if err := resourceLoadBalancerV2SecurityGroups(networkingClient, lb.VipPortID, d); err != nil {
   154  		return err
   155  	}
   156  
   157  	// If all has been successful, set the ID on the resource
   158  	d.SetId(lb.ID)
   159  
   160  	return resourceLoadBalancerV2Read(d, meta)
   161  }
   162  
   163  func resourceLoadBalancerV2Read(d *schema.ResourceData, meta interface{}) error {
   164  	config := meta.(*Config)
   165  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   166  	if err != nil {
   167  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   168  	}
   169  
   170  	lb, err := loadbalancers.Get(networkingClient, d.Id()).Extract()
   171  	if err != nil {
   172  		return CheckDeleted(d, err, "LoadBalancerV2")
   173  	}
   174  
   175  	log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 LoadBalancer %s: %+v", d.Id(), lb)
   176  
   177  	d.Set("name", lb.Name)
   178  	d.Set("description", lb.Description)
   179  	d.Set("vip_subnet_id", lb.VipSubnetID)
   180  	d.Set("tenant_id", lb.TenantID)
   181  	d.Set("vip_address", lb.VipAddress)
   182  	d.Set("vip_port_id", lb.VipPortID)
   183  	d.Set("admin_state_up", lb.AdminStateUp)
   184  	d.Set("flavor", lb.Flavor)
   185  	d.Set("loadbalancer_provider", lb.Provider)
   186  
   187  	// Get any security groups on the VIP Port
   188  	if lb.VipPortID != "" {
   189  		port, err := ports.Get(networkingClient, lb.VipPortID).Extract()
   190  		if err != nil {
   191  			return err
   192  		}
   193  
   194  		d.Set("security_group_ids", port.SecurityGroups)
   195  	}
   196  
   197  	return nil
   198  }
   199  
   200  func resourceLoadBalancerV2Update(d *schema.ResourceData, meta interface{}) error {
   201  	config := meta.(*Config)
   202  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   203  	if err != nil {
   204  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   205  	}
   206  
   207  	var updateOpts loadbalancers.UpdateOpts
   208  	if d.HasChange("name") {
   209  		updateOpts.Name = d.Get("name").(string)
   210  	}
   211  	if d.HasChange("description") {
   212  		updateOpts.Description = d.Get("description").(string)
   213  	}
   214  	if d.HasChange("admin_state_up") {
   215  		asu := d.Get("admin_state_up").(bool)
   216  		updateOpts.AdminStateUp = &asu
   217  	}
   218  
   219  	log.Printf("[DEBUG] Updating OpenStack LBaaSV2 LoadBalancer %s with options: %+v", d.Id(), updateOpts)
   220  
   221  	_, err = loadbalancers.Update(networkingClient, d.Id(), updateOpts).Extract()
   222  	if err != nil {
   223  		return fmt.Errorf("Error updating OpenStack LBaaSV2 LoadBalancer: %s", err)
   224  	}
   225  
   226  	// Security Groups get updated separately
   227  	if d.HasChange("security_group_ids") {
   228  		vipPortID := d.Get("vip_port_id").(string)
   229  		if err := resourceLoadBalancerV2SecurityGroups(networkingClient, vipPortID, d); err != nil {
   230  			return err
   231  		}
   232  	}
   233  
   234  	return resourceLoadBalancerV2Read(d, meta)
   235  }
   236  
   237  func resourceLoadBalancerV2Delete(d *schema.ResourceData, meta interface{}) error {
   238  	config := meta.(*Config)
   239  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   240  	if err != nil {
   241  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   242  	}
   243  
   244  	stateConf := &resource.StateChangeConf{
   245  		Pending:    []string{"ACTIVE", "PENDING_DELETE"},
   246  		Target:     []string{"DELETED"},
   247  		Refresh:    waitForLoadBalancerDelete(networkingClient, d.Id()),
   248  		Timeout:    2 * time.Minute,
   249  		Delay:      5 * time.Second,
   250  		MinTimeout: 3 * time.Second,
   251  	}
   252  
   253  	_, err = stateConf.WaitForState()
   254  	if err != nil {
   255  		return fmt.Errorf("Error deleting OpenStack LBaaSV2 LoadBalancer: %s", err)
   256  	}
   257  
   258  	d.SetId("")
   259  	return nil
   260  }
   261  
   262  func resourceLoadBalancerV2SecurityGroups(networkingClient *gophercloud.ServiceClient, vipPortID string, d *schema.ResourceData) error {
   263  	if vipPortID != "" {
   264  		if _, ok := d.GetOk("security_group_ids"); ok {
   265  			updateOpts := ports.UpdateOpts{
   266  				SecurityGroups: resourcePortSecurityGroupsV2(d),
   267  			}
   268  
   269  			log.Printf("[DEBUG] Adding security groups to OpenStack LoadBalancer "+
   270  				"VIP Port (%s): %#v", vipPortID, updateOpts)
   271  
   272  			_, err := ports.Update(networkingClient, vipPortID, updateOpts).Extract()
   273  			if err != nil {
   274  				return err
   275  			}
   276  		}
   277  	}
   278  
   279  	return nil
   280  }
   281  
   282  func waitForLoadBalancerActive(networkingClient *gophercloud.ServiceClient, lbID string) resource.StateRefreshFunc {
   283  	return func() (interface{}, string, error) {
   284  		lb, err := loadbalancers.Get(networkingClient, lbID).Extract()
   285  		if err != nil {
   286  			return nil, "", err
   287  		}
   288  
   289  		log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer: %+v", lb)
   290  		if lb.ProvisioningStatus == "ACTIVE" {
   291  			return lb, "ACTIVE", nil
   292  		}
   293  
   294  		return lb, lb.ProvisioningStatus, nil
   295  	}
   296  }
   297  
   298  func waitForLoadBalancerDelete(networkingClient *gophercloud.ServiceClient, lbID string) resource.StateRefreshFunc {
   299  	return func() (interface{}, string, error) {
   300  		log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 LoadBalancer %s", lbID)
   301  
   302  		lb, err := loadbalancers.Get(networkingClient, lbID).Extract()
   303  		if err != nil {
   304  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   305  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 LoadBalancer %s", lbID)
   306  				return lb, "DELETED", nil
   307  			}
   308  			return lb, "ACTIVE", err
   309  		}
   310  
   311  		log.Printf("[DEBUG] Openstack LoadBalancerV2: %+v", lb)
   312  		err = loadbalancers.Delete(networkingClient, lbID).ExtractErr()
   313  		if err != nil {
   314  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   315  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 LoadBalancer %s", lbID)
   316  				return lb, "DELETED", nil
   317  			}
   318  
   319  			if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
   320  				if errCode.Actual == 409 {
   321  					log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer (%s) is still in use.", lbID)
   322  					return lb, "ACTIVE", nil
   323  				}
   324  			}
   325  
   326  			return lb, "ACTIVE", err
   327  		}
   328  
   329  		log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer (%s) still active.", lbID)
   330  		return lb, "ACTIVE", nil
   331  	}
   332  }