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 }