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