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 }