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