github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/openstack/resource_openstack_lb_pool_v1.go (about) 1 package openstack 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/hashcode" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 13 "github.com/gophercloud/gophercloud" 14 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members" 15 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools" 16 "github.com/gophercloud/gophercloud/pagination" 17 ) 18 19 func resourceLBPoolV1() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceLBPoolV1Create, 22 Read: resourceLBPoolV1Read, 23 Update: resourceLBPoolV1Update, 24 Delete: resourceLBPoolV1Delete, 25 Importer: &schema.ResourceImporter{ 26 State: schema.ImportStatePassthrough, 27 }, 28 29 Timeouts: &schema.ResourceTimeout{ 30 Create: schema.DefaultTimeout(10 * time.Minute), 31 Delete: schema.DefaultTimeout(10 * time.Minute), 32 }, 33 34 Schema: map[string]*schema.Schema{ 35 "region": &schema.Schema{ 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 40 }, 41 "name": &schema.Schema{ 42 Type: schema.TypeString, 43 Required: true, 44 ForceNew: false, 45 }, 46 "protocol": &schema.Schema{ 47 Type: schema.TypeString, 48 Required: true, 49 ForceNew: true, 50 }, 51 "subnet_id": &schema.Schema{ 52 Type: schema.TypeString, 53 Required: true, 54 ForceNew: true, 55 }, 56 57 "lb_method": &schema.Schema{ 58 Type: schema.TypeString, 59 Required: true, 60 ForceNew: false, 61 }, 62 "lb_provider": &schema.Schema{ 63 Type: schema.TypeString, 64 Optional: true, 65 Computed: true, 66 ForceNew: true, 67 }, 68 "tenant_id": &schema.Schema{ 69 Type: schema.TypeString, 70 Optional: true, 71 ForceNew: true, 72 Computed: true, 73 }, 74 "member": &schema.Schema{ 75 Type: schema.TypeSet, 76 Deprecated: "Use openstack_lb_member_v1 instead. This attribute will be removed in a future version.", 77 Optional: true, 78 Elem: &schema.Resource{ 79 Schema: map[string]*schema.Schema{ 80 "region": &schema.Schema{ 81 Type: schema.TypeString, 82 Required: true, 83 ForceNew: true, 84 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 85 }, 86 "tenant_id": &schema.Schema{ 87 Type: schema.TypeString, 88 Optional: true, 89 ForceNew: true, 90 }, 91 "address": &schema.Schema{ 92 Type: schema.TypeString, 93 Required: true, 94 ForceNew: true, 95 }, 96 "port": &schema.Schema{ 97 Type: schema.TypeInt, 98 Required: true, 99 ForceNew: true, 100 }, 101 "admin_state_up": &schema.Schema{ 102 Type: schema.TypeBool, 103 Required: true, 104 ForceNew: false, 105 }, 106 }, 107 }, 108 Set: resourceLBMemberV1Hash, 109 }, 110 "monitor_ids": &schema.Schema{ 111 Type: schema.TypeSet, 112 Optional: true, 113 ForceNew: false, 114 Elem: &schema.Schema{Type: schema.TypeString}, 115 Set: schema.HashString, 116 }, 117 }, 118 } 119 } 120 121 func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error { 122 config := meta.(*Config) 123 networkingClient, err := config.networkingV2Client(GetRegion(d)) 124 if err != nil { 125 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 126 } 127 128 createOpts := pools.CreateOpts{ 129 Name: d.Get("name").(string), 130 SubnetID: d.Get("subnet_id").(string), 131 TenantID: d.Get("tenant_id").(string), 132 Provider: d.Get("lb_provider").(string), 133 } 134 135 if v, ok := d.GetOk("protocol"); ok { 136 protocol := resourceLBPoolV1DetermineProtocol(v.(string)) 137 createOpts.Protocol = protocol 138 } 139 140 if v, ok := d.GetOk("lb_method"); ok { 141 lbMethod := resourceLBPoolV1DetermineLBMethod(v.(string)) 142 createOpts.LBMethod = lbMethod 143 } 144 145 log.Printf("[DEBUG] Create Options: %#v", createOpts) 146 p, err := pools.Create(networkingClient, createOpts).Extract() 147 if err != nil { 148 return fmt.Errorf("Error creating OpenStack LB pool: %s", err) 149 } 150 log.Printf("[INFO] LB Pool ID: %s", p.ID) 151 152 log.Printf("[DEBUG] Waiting for OpenStack LB pool (%s) to become available.", p.ID) 153 154 stateConf := &resource.StateChangeConf{ 155 Pending: []string{"PENDING_CREATE"}, 156 Target: []string{"ACTIVE"}, 157 Refresh: waitForLBPoolActive(networkingClient, p.ID), 158 Timeout: d.Timeout(schema.TimeoutCreate), 159 Delay: 5 * time.Second, 160 MinTimeout: 3 * time.Second, 161 } 162 163 _, err = stateConf.WaitForState() 164 if err != nil { 165 return err 166 } 167 168 d.SetId(p.ID) 169 170 if mIDs := resourcePoolMonitorIDsV1(d); mIDs != nil { 171 for _, mID := range mIDs { 172 _, err := pools.AssociateMonitor(networkingClient, p.ID, mID).Extract() 173 if err != nil { 174 return fmt.Errorf("Error associating monitor (%s) with OpenStack LB pool (%s): %s", mID, p.ID, err) 175 } 176 } 177 } 178 179 if memberOpts := resourcePoolMembersV1(d); memberOpts != nil { 180 for _, memberOpt := range memberOpts { 181 _, err := members.Create(networkingClient, memberOpt).Extract() 182 if err != nil { 183 return fmt.Errorf("Error creating OpenStack LB member: %s", err) 184 } 185 } 186 } 187 188 return resourceLBPoolV1Read(d, meta) 189 } 190 191 func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { 192 config := meta.(*Config) 193 networkingClient, err := config.networkingV2Client(GetRegion(d)) 194 if err != nil { 195 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 196 } 197 198 p, err := pools.Get(networkingClient, d.Id()).Extract() 199 if err != nil { 200 return CheckDeleted(d, err, "LB pool") 201 } 202 203 log.Printf("[DEBUG] Retrieved OpenStack LB Pool %s: %+v", d.Id(), p) 204 205 d.Set("name", p.Name) 206 d.Set("protocol", p.Protocol) 207 d.Set("subnet_id", p.SubnetID) 208 d.Set("lb_method", p.LBMethod) 209 d.Set("lb_provider", p.Provider) 210 d.Set("tenant_id", p.TenantID) 211 d.Set("monitor_ids", p.MonitorIDs) 212 d.Set("member_ids", p.MemberIDs) 213 d.Set("region", GetRegion(d)) 214 215 return nil 216 } 217 218 func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error { 219 config := meta.(*Config) 220 networkingClient, err := config.networkingV2Client(GetRegion(d)) 221 if err != nil { 222 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 223 } 224 225 var updateOpts pools.UpdateOpts 226 // If either option changed, update both. 227 // Gophercloud complains if one is empty. 228 if d.HasChange("name") || d.HasChange("lb_method") { 229 updateOpts.Name = d.Get("name").(string) 230 231 lbMethod := resourceLBPoolV1DetermineLBMethod(d.Get("lb_method").(string)) 232 updateOpts.LBMethod = lbMethod 233 } 234 235 log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts) 236 237 _, err = pools.Update(networkingClient, d.Id(), updateOpts).Extract() 238 if err != nil { 239 return fmt.Errorf("Error updating OpenStack LB Pool: %s", err) 240 } 241 242 if d.HasChange("monitor_ids") { 243 oldMIDsRaw, newMIDsRaw := d.GetChange("monitor_ids") 244 oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set) 245 monitorsToAdd := newMIDsSet.Difference(oldMIDsSet) 246 monitorsToRemove := oldMIDsSet.Difference(newMIDsSet) 247 248 log.Printf("[DEBUG] Monitors to add: %v", monitorsToAdd) 249 250 log.Printf("[DEBUG] Monitors to remove: %v", monitorsToRemove) 251 252 for _, m := range monitorsToAdd.List() { 253 _, err := pools.AssociateMonitor(networkingClient, d.Id(), m.(string)).Extract() 254 if err != nil { 255 return fmt.Errorf("Error associating monitor (%s) with OpenStack server (%s): %s", m.(string), d.Id(), err) 256 } 257 log.Printf("[DEBUG] Associated monitor (%s) with pool (%s)", m.(string), d.Id()) 258 } 259 260 for _, m := range monitorsToRemove.List() { 261 _, err := pools.DisassociateMonitor(networkingClient, d.Id(), m.(string)).Extract() 262 if err != nil { 263 return fmt.Errorf("Error disassociating monitor (%s) from OpenStack server (%s): %s", m.(string), d.Id(), err) 264 } 265 log.Printf("[DEBUG] Disassociated monitor (%s) from pool (%s)", m.(string), d.Id()) 266 } 267 } 268 269 if d.HasChange("member") { 270 oldMembersRaw, newMembersRaw := d.GetChange("member") 271 oldMembersSet, newMembersSet := oldMembersRaw.(*schema.Set), newMembersRaw.(*schema.Set) 272 membersToAdd := newMembersSet.Difference(oldMembersSet) 273 membersToRemove := oldMembersSet.Difference(newMembersSet) 274 275 log.Printf("[DEBUG] Members to add: %v", membersToAdd) 276 277 log.Printf("[DEBUG] Members to remove: %v", membersToRemove) 278 279 for _, m := range membersToRemove.List() { 280 oldMember := resourcePoolMemberV1(d, m) 281 listOpts := members.ListOpts{ 282 PoolID: d.Id(), 283 Address: oldMember.Address, 284 ProtocolPort: oldMember.ProtocolPort, 285 } 286 err = members.List(networkingClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { 287 extractedMembers, err := members.ExtractMembers(page) 288 if err != nil { 289 return false, err 290 } 291 for _, member := range extractedMembers { 292 err := members.Delete(networkingClient, member.ID).ExtractErr() 293 if err != nil { 294 return false, fmt.Errorf("Error deleting member (%s) from OpenStack LB pool (%s): %s", member.ID, d.Id(), err) 295 } 296 log.Printf("[DEBUG] Deleted member (%s) from pool (%s)", member.ID, d.Id()) 297 } 298 return true, nil 299 }) 300 } 301 302 for _, m := range membersToAdd.List() { 303 createOpts := resourcePoolMemberV1(d, m) 304 newMember, err := members.Create(networkingClient, createOpts).Extract() 305 if err != nil { 306 return fmt.Errorf("Error creating LB member: %s", err) 307 } 308 log.Printf("[DEBUG] Created member (%s) in OpenStack LB pool (%s)", newMember.ID, d.Id()) 309 } 310 } 311 312 return resourceLBPoolV1Read(d, meta) 313 } 314 315 func resourceLBPoolV1Delete(d *schema.ResourceData, meta interface{}) error { 316 config := meta.(*Config) 317 networkingClient, err := config.networkingV2Client(GetRegion(d)) 318 if err != nil { 319 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 320 } 321 322 // Make sure all monitors are disassociated first 323 if v, ok := d.GetOk("monitor_ids"); ok { 324 if monitorIDList, ok := v.([]interface{}); ok { 325 for _, monitorID := range monitorIDList { 326 mID := monitorID.(string) 327 log.Printf("[DEBUG] Attempting to disassociate monitor %s from pool %s", mID, d.Id()) 328 if res := pools.DisassociateMonitor(networkingClient, d.Id(), mID); res.Err != nil { 329 return fmt.Errorf("Error disassociating monitor %s from pool %s: %s", mID, d.Id(), err) 330 } 331 } 332 } 333 } 334 335 stateConf := &resource.StateChangeConf{ 336 Pending: []string{"ACTIVE", "PENDING_DELETE"}, 337 Target: []string{"DELETED"}, 338 Refresh: waitForLBPoolDelete(networkingClient, d.Id()), 339 Timeout: d.Timeout(schema.TimeoutDelete), 340 Delay: 5 * time.Second, 341 MinTimeout: 3 * time.Second, 342 } 343 344 _, err = stateConf.WaitForState() 345 if err != nil { 346 return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err) 347 } 348 349 d.SetId("") 350 return nil 351 } 352 353 func resourcePoolMonitorIDsV1(d *schema.ResourceData) []string { 354 mIDsRaw := d.Get("monitor_ids").(*schema.Set) 355 mIDs := make([]string, mIDsRaw.Len()) 356 for i, raw := range mIDsRaw.List() { 357 mIDs[i] = raw.(string) 358 } 359 return mIDs 360 } 361 362 func resourcePoolMembersV1(d *schema.ResourceData) []members.CreateOpts { 363 memberOptsRaw := d.Get("member").(*schema.Set) 364 memberOpts := make([]members.CreateOpts, memberOptsRaw.Len()) 365 for i, raw := range memberOptsRaw.List() { 366 rawMap := raw.(map[string]interface{}) 367 memberOpts[i] = members.CreateOpts{ 368 TenantID: rawMap["tenant_id"].(string), 369 Address: rawMap["address"].(string), 370 ProtocolPort: rawMap["port"].(int), 371 PoolID: d.Id(), 372 } 373 } 374 return memberOpts 375 } 376 377 func resourcePoolMemberV1(d *schema.ResourceData, raw interface{}) members.CreateOpts { 378 rawMap := raw.(map[string]interface{}) 379 return members.CreateOpts{ 380 TenantID: rawMap["tenant_id"].(string), 381 Address: rawMap["address"].(string), 382 ProtocolPort: rawMap["port"].(int), 383 PoolID: d.Id(), 384 } 385 } 386 387 func resourceLBMemberV1Hash(v interface{}) int { 388 var buf bytes.Buffer 389 m := v.(map[string]interface{}) 390 buf.WriteString(fmt.Sprintf("%s-", m["region"].(string))) 391 buf.WriteString(fmt.Sprintf("%s-", m["tenant_id"].(string))) 392 buf.WriteString(fmt.Sprintf("%s-", m["address"].(string))) 393 buf.WriteString(fmt.Sprintf("%d-", m["port"].(int))) 394 395 return hashcode.String(buf.String()) 396 } 397 398 func resourceLBPoolV1DetermineProtocol(v string) pools.LBProtocol { 399 var protocol pools.LBProtocol 400 switch v { 401 case "TCP": 402 protocol = pools.ProtocolTCP 403 case "HTTP": 404 protocol = pools.ProtocolHTTP 405 case "HTTPS": 406 protocol = pools.ProtocolHTTPS 407 } 408 409 return protocol 410 } 411 412 func resourceLBPoolV1DetermineLBMethod(v string) pools.LBMethod { 413 var lbMethod pools.LBMethod 414 switch v { 415 case "ROUND_ROBIN": 416 lbMethod = pools.LBMethodRoundRobin 417 case "LEAST_CONNECTIONS": 418 lbMethod = pools.LBMethodLeastConnections 419 } 420 421 return lbMethod 422 } 423 424 func waitForLBPoolActive(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc { 425 return func() (interface{}, string, error) { 426 p, err := pools.Get(networkingClient, poolId).Extract() 427 if err != nil { 428 return nil, "", err 429 } 430 431 log.Printf("[DEBUG] OpenStack LB Pool: %+v", p) 432 if p.Status == "ACTIVE" { 433 return p, "ACTIVE", nil 434 } 435 436 return p, p.Status, nil 437 } 438 } 439 440 func waitForLBPoolDelete(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc { 441 return func() (interface{}, string, error) { 442 log.Printf("[DEBUG] Attempting to delete OpenStack LB Pool %s", poolId) 443 444 p, err := pools.Get(networkingClient, poolId).Extract() 445 if err != nil { 446 if _, ok := err.(gophercloud.ErrDefault404); ok { 447 log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId) 448 return p, "DELETED", nil 449 } 450 return p, "ACTIVE", err 451 } 452 453 log.Printf("[DEBUG] OpenStack LB Pool: %+v", p) 454 err = pools.Delete(networkingClient, poolId).ExtractErr() 455 if err != nil { 456 if _, ok := err.(gophercloud.ErrDefault404); ok { 457 log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId) 458 return p, "DELETED", nil 459 } 460 return p, "ACTIVE", err 461 } 462 463 log.Printf("[DEBUG] OpenStack LB Pool %s still active.", poolId) 464 return p, "ACTIVE", nil 465 } 466 467 }