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