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 }