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